A vulnerability scanner that runs only when someone remembers to launch it is not a program. It is a habit, and habits break under load. As a security team adds integrations, the number of scans in flight at any moment grows from a handful to dozens, then hundreds. At that scale, two quiet failures start to compound. Scans stop running on the cadence the team intended, and the picture inside the management platform drifts away from what the scanners are actually doing. Both failures share the same root cause. They depend on a human pressing a button at the right moment.
This article is about removing that dependency. PMAP treats scheduling and live synchronization as a single automation layer that keeps the scan inventory current, keeps running scans visible in real time, and keeps the platform a faithful mirror of every connected vendor. It is the operational glue that lets a program scale past the point where manual refresh and manual registration stop working. If you want the broader context first, this layer sits inside the full multi-vendor scan orchestration model. This piece narrows in on the part that runs while no one is watching.
Why Manual Refresh and Manual Registration Do Not Scale
Picture a program with a dozen connected integrations, each running scans on its own schedule. A Nessus appliance kicks off a nightly internal sweep. A Qualys subscription runs authenticated checks across several asset groups. A Rapid7 InsightVM console fires ad hoc scans whenever an engineer launches one from the vendor UI directly. Each of these produces execution state that a vulnerability management team needs to see. Is the scan still running? Did it complete? How many findings did it produce, broken down by severity?
When that state lives only in each vendor console, an analyst has two bad options. They can hop between consoles to check status, which fragments attention and wastes time, or they can wait for someone to manually import results into the management platform, which means the platform is always a step behind reality. Neither option survives contact with a real enterprise workload.
Manual scan registration has the same problem in a different shape. If an engineer launches a scan directly in Qualys, that scan exists, it is producing data, and yet PMAP has no record of it until a human creates one. The gap between “the scanner knows” and “the platform knows” is exactly where coverage assumptions quietly fail. A scan that the platform cannot see cannot be triaged, cannot feed correlation, and cannot show up in a coverage report.
PMAP closes both gaps with background automation. A cron scheduler fires imports on a defined cadence without anyone pressing a button. A live status ticker keeps execution state fresh. An orphan adoption sweep discovers scans the platform has not seen yet and pulls them in automatically. The rest of this article walks through each mechanism and explains the behavior you can rely on.
Cron Scheduling for Hands-Free Recurring Imports
The first piece of the automation layer is the cron scheduler. Any scan record can carry a schedule_cron value, a standard cron expression that describes when the import pipeline should run. A nightly two o’clock import, for example, is expressed as 0 2 * * *. Once that value is set, the scan no longer needs a human trigger.
Under the hood, PMAP runs a long-lived scheduler goroutine that loads every scan with a non-null schedule_cron value and registers each one with the robfig/cron engine. When a scheduled time arrives, the scheduler calls the import pipeline for that scan automatically. Each scheduled run executes with a ten-minute context timeout, which bounds how long any single import is allowed to occupy the worker before it is cut off. The trigger is attributed to a fixed system-user UUID rather than to a person, so the activity log records honestly that the run was machine-initiated rather than analyst-initiated.
One behavior is worth internalizing because it shapes how you operate. The scheduler reloads its set of scheduled scans every five minutes. That means a new schedule_cron value saved on an existing scan does not take effect the instant you save it. It takes effect within five minutes, on the next reload cycle. This is not a bug to design around. It is a predictable property. If you set a schedule a few minutes before the intended fire time, give it a moment to register before you expect it to run. For any normal recurring cadence measured in hours or days, the five-minute reload window is invisible.
The practical payoff is that recurring assessments become hands-free. You decide the cadence once, you encode it as a cron expression, and the import pipeline runs on that cadence indefinitely. There is no nightly reminder, no runbook step that says “remember to import,” and no single point of human failure standing between a completed vendor scan and a refreshed finding set inside the platform. A continuous remediation program depends on a continuous intake cadence, a point reinforced by frameworks such as the NIST guidance on enterprise vulnerability management, and cron scheduling is how PMAP makes that cadence automatic.
Live Status Sync Every 30 Seconds
Scheduling decides when scans run. Live status sync decides how quickly you find out what is happening once they do. PMAP runs a background ticker that wakes every thirty seconds and polls the vendor for the current state of any scan that is still in motion.
The ticker is deliberately scoped. It targets scans whose status is running, queued, paused, or importing, and only those that are integration-backed, meaning they carry an integration_id pointing at a connected vendor. A completed scan does not need polling, so it is left alone. A manual scan with no vendor link has no remote state to fetch, so it is skipped too. This scoping keeps the polling load proportional to the work actually in flight rather than to the total size of the scan history.
On each tick, for every qualifying scan, the loop reads fresh state from the vendor and writes it back to the scan record. That includes the translated status, the raw remote_status string exactly as the vendor reported it, the progress percentage, the host_count, the findings_count, and the per-severity counters. The thirty-second cadence sets the ceiling on staleness. An analyst watching a running scan sees its state reflected within thirty seconds of any change on the vendor side, without ever opening the vendor console.
There is one important consequence of the loop noticing a completed scan. When the live sync writes a status of completed for a scan that was previously running, it does not stop at the status field. It auto-triggers the rest of the pipeline. Asset import, findings import, and enrichment all fire in background goroutines. The completion of a vendor scan is, in effect, the event that pulls its results into PMAP. No separate manual import step stands between “the scanner finished” and “the findings are in the platform.” We will come back to that auto-import behavior in its own section, because it deserves a closer look.
Vendor to PMAP Status Mapping
Different scanners speak different status dialects. One vendor says canceled, another says cancelled, a third reports error where you might expect failed, and Nessus reports an empty string for a scan that has been created but not yet started. If PMAP surfaced these raw strings directly, every dashboard, filter, and report would have to understand every vendor’s vocabulary. That does not scale, and it leaks vendor specifics into places that should be vendor-neutral.
PMAP solves this with a translation table that normalizes vendor strings into a single canonical status set before anything else reads them. The mapping is direct and predictable. A vendor running maps to running, paused maps to paused, and completed maps to completed. Both spellings of cancellation, canceled and cancelled, collapse into the canonical cancelled. A vendor error maps to failed. A vendor queued maps to queued, and the empty status that Nessus emits for a not-yet-started scan also maps to queued, which is the most honest interpretation of “created but not running.”
Because this translation happens once, in the sync layer, every downstream consumer works against one vocabulary. A filter for running scans works identically whether the underlying scanner is Nessus, Qualys, or Rapid7. The raw vendor string is still preserved in remote_status for anyone who needs the unfiltered truth, so normalization adds clarity without discarding detail.
Auto-Import on the Completed Transition
The moment a scan transitions to completed is the most consequential event in its lifecycle, because completion is what turns a running process into a usable finding set. PMAP treats that transition as a trigger rather than a destination. When the live sync loop writes status = completed, it immediately kicks off three pieces of work in the background. It imports the assets the scan touched, it imports the findings the scan produced, and it runs enrichment over those findings.
Running this work in background goroutines matters for responsiveness. The sync loop is not blocked while a large import grinds through thousands of findings. It records the completion, fires the import, and moves on to its next tick. The import proceeds independently, and the findings appear in PMAP when it finishes. From the analyst’s perspective, a scan they were watching simply transitions from “running” to “completed,” and shortly afterward its findings are available for triage, with no manual import click anywhere in the sequence.
This is the behavior that makes scheduling genuinely hands-free end to end. A cron-scheduled scan fires automatically, the live sync notices when the vendor reports it complete, and auto-import pulls the results in. The full path from “it is time to scan” to “the findings are in the queue” runs without a human in the loop. The same auto-import behavior applies to scans the platform discovers on its own, which is the next mechanism to understand.
Orphan Adoption: Discover Scans PMAP Has Not Seen
Scheduling and live sync both assume PMAP already knows about a scan. Orphan adoption handles the case where it does not. Engineers launch scans directly from vendor consoles all the time. Someone runs an ad hoc Qualys scan to check a freshly deployed service. A penetration tester fires a Nessus sweep before an engagement. These scans are real, they produce findings, and PMAP has no row for them until it goes looking.
Every five minutes, a background sweep does exactly that. For each active integration, PMAP fetches the vendor’s full list of scans and compares it against what it already has. Any scan that exists on the vendor but has no corresponding PMAP row, and is not on the blocklist, gets adopted. A new scan row is created automatically, pulling that previously invisible scan into the platform’s inventory.
Adoption is not just bookkeeping. If an adopted scan is already in a completed state on the vendor side, PMAP immediately fires asset import, findings import, and enrichment for it, just as it would for a scan that completed under live sync. So a scan an engineer ran an hour ago in the vendor console can be fully present in PMAP, findings and all, within the next sweep cycle, with zero manual registration. This is what keeps the Scans list a complete picture rather than a partial one shaped by who remembered to register what.
The five-minute cadence is the trade-off here. Orphan adoption is not instantaneous. A scan launched directly on the vendor appears in PMAP within roughly five minutes, on the next sweep, rather than the instant it starts. For continuous discovery that runs in the background, this is a reasonable cadence that balances freshness against the cost of repeatedly querying every vendor’s full scan list. If you cannot wait for the next sweep, there is an on-demand path, covered further down.
The Blocklist Guard Against Re-adoption
Automatic discovery introduces an obvious risk. If PMAP keeps pulling in every scan it finds on the vendor, what happens when an analyst deliberately deletes a scan? Without a guard, the very next orphan adoption sweep would see that scan still present on the vendor side and helpfully re-create it. The analyst’s deletion would be silently undone, and the scan would reappear like a weed.
PMAP prevents this with a blocklist. When a scan is deleted, before the delete request even returns, PMAP writes the scan’s (integration_id, external_scan_id) pair to a blocklist. Every subsequent orphan adoption pass checks that blocklist and skips any entry on it. The vendor may keep reporting that scan in its list for as long as it likes. PMAP will see it, recognize the pair as intentionally removed, and decline to re-adopt it.
The principle is simple and worth stating plainly. Deleted stays deleted. An analyst’s decision to remove a scan is durable against the automation that would otherwise reverse it. This is the kind of behavior that builds trust in background processes. Automation that quietly undoes deliberate human actions teaches teams to distrust it. The blocklist guard ensures orphan adoption is purely additive. It fills gaps, and it never resurrects something you removed on purpose.
Real-Time UI: WebSocket Push and 8-Second Polling
Background sync keeps the data fresh on the server. The Scan Detail page makes that freshness visible to the analyst without anyone hitting refresh. It does this through two complementary mechanisms working together.
The first is a real-time push. The Scan Detail page subscribes to scan.updated WebSocket events. When the backend finishes an import and emits the underlying event, a bridge republishes it as a scan.updated push, and the page reacts by invalidating its cached view of that scan. The status badge, the progress bar, and the KPI row re-render with the new state, all without a full-page reload. The analyst sees the scan move on its own.
The second mechanism is short-interval polling as a safety net. While a scan is in a running or importing or queued state, the relevant tabs on the Scan Detail page poll every eight seconds. Push events deliver near-instant updates when they arrive, and the eight-second poll guarantees the view never sits stale even if a particular push is missed. The two together give a live, self-updating view of an in-flight scan. The server-side mechanics that carry these real-time events are themselves a topic, covered in the dedicated piece on PMAP’s real-time event delivery.
For an analyst, the practical effect is that the Scan Detail page behaves like a live dashboard rather than a static report. Open it on a running scan and watch the progress bar advance, the severity counts climb, and the status flip to completed, all in place. There is no reflex to refresh because there is nothing to refresh away.
On-Demand Sync When You Cannot Wait
Background cadences are the right default for steady-state operation, but there are moments when waiting up to five minutes for the next sweep is too long. An executive review is about to start and you need the absolute latest status. You just deleted a scan and want to confirm the state immediately. For these moments, PMAP exposes an on-demand sync.
The Refresh button on the Scans list, and the POST /sync API behind it, fire an immediate vendor status poll and orphan adoption pass on demand. You do not have to wait for the next scheduled tick. Optionally, the call can be scoped to a single integration, so you can refresh just the vendor you care about rather than every connected one.
Two design choices make on-demand sync robust. First, the endpoint responds with HTTP 202 immediately. It accepts the request and returns at once, so the UI is never blocked waiting for a potentially slow round trip across multiple vendors. The actual sync work happens in the background. Second, that background work runs under a detached context rather than the request’s own context. If the user navigates away or the HTTP request is cancelled, the sync goroutine keeps running to completion. A sync you asked for finishes even if you close the tab, which is exactly the behavior you want from a “make it current now” action.
On-demand sync is the manual override on top of an otherwise automatic system. It does not replace the background cadence. It complements it, giving an analyst a way to force freshness at the exact moment they need it, while the scheduled sweeps continue to handle the steady state.
The Findings Delta Widget After Every Wave
Keeping scans current is only useful if a team can quickly read what changed. After every import, the question an analyst actually wants answered is not “how many findings are there” but “what is different since last time.” The Scan Detail “What changed” delta widget answers exactly that.
The widget compares the scan’s current results against prior wave-matrix data and breaks the difference down three ways, sliced by severity. It shows the new findings that appeared in this wave, the regressions where a previously resolved issue has returned, and the total counts as they stand now. Seeing those three numbers per severity turns a raw import into an immediately legible diff. A handful of new critical findings is a different operational signal from a wave that is mostly regressions, and the widget makes that distinction visible at a glance rather than requiring a manual comparison.
The widget appears on the Scan Detail page when the scan has findings and there is prior wave data to compare against. It is the natural reading surface after an import completes, whether that import was triggered by a cron schedule, a live-sync completion, an orphan adoption, or an on-demand sync. Whatever pulled the data in, the delta widget is how an analyst reads the result. Understanding what counts as a regression versus a genuinely new finding ties back into how PMAP correlates and deduplicates findings at ingest, which is its own deep topic worth reading alongside this one.
Completed-Meta Backfill for Stale Counts
There is one quiet maintenance behavior that keeps the data honest over time. Some scans, particularly those adopted before per-severity counting existed or those that landed in odd states, can show zero severity counts even though they are completed and do have findings. A platform that left those zeros in place would be subtly lying on every dashboard that summed severity counts across scans.
PMAP runs a completed-meta backfill sweep every five minutes to correct this. The sweep looks for completed adopted scans that still display zero per-severity counts and refreshes those counts from the actual findings in the database. Over successive sweeps, stale zeros are replaced with accurate numbers.
The sweep is deliberately throttled. It processes at most twenty scans per run. That cap exists to avoid hammering the database, especially during onboarding when a large historical backlog might need backfilling all at once. The behavior you should expect from this cap is gradual drain. A large backlog of scans needing backfill does not get fixed in a single pass. It is corrected steadily over successive five-minute ticks, twenty at a time, until the backlog is cleared. This is a conscious choice to favor database health over instant correction, and for a background maintenance task that bias is the right one.
How PMAP Stays a Self-Maintaining Mirror
Step back from the individual mechanisms and a single design philosophy comes into focus. PMAP aims to be a faithful, self-maintaining mirror of every connected vendor’s scan portfolio. Each piece of the automation layer serves that goal from a different angle.
Cron scheduling ensures scans run on the intended cadence without a human trigger. Live status sync ensures running scans never sit unattended, with state refreshed every thirty seconds. Auto-import on completion ensures finished scans become usable finding sets without a manual import step. Orphan adoption ensures scans launched outside PMAP are discovered and pulled in within five minutes, so the inventory is complete rather than partial. The blocklist guard ensures that completeness never overrides a deliberate deletion. Real-time UI updates ensure analysts see all of this happen live. On-demand sync gives a manual override for the moments that cannot wait. And completed-meta backfill quietly keeps historical counts honest.
The result is a scan inventory that maintains itself. An engineer sets a schedule once and never touches import again. An analyst opens a scan and trusts that what they see matches the vendor without checking a second console. A platform administrator trusts that the Scans list is the whole picture, not a subset shaped by who remembered to register what. That trust is the real product of this layer, and it is measurable. PMAP tracks scheduler reliability, orphan adoption lag, sync staleness, and how often analysts feel the need to click Refresh, the last of which is a useful proxy for how much they trust the background sync to do its job.
A continuous vulnerability management program is exactly the cadence that controls frameworks like the CIS Controls continuous vulnerability management control describe, and it cannot rest on manual button-pressing. It needs an automation layer underneath that keeps running while no one is watching. That is what scheduling and live sync provide. To see how this layer fits into PMAP’s wider intake and correlation story, read the full multi-vendor scan orchestration model. For a step-by-step setup walkthrough on a specific vendor, the guide to import Qualys VM with live sync enabled covers the hands-on configuration.
Frequently Asked Questions
How quickly does PMAP reflect a running scan’s status?
The live status sync ticker runs every thirty seconds. It polls all integration-backed scans in a running, queued, paused, or importing state and writes fresh status, progress, findings count, and per-severity counts. So an analyst sees a running scan’s current state reflected within thirty seconds of any change on the vendor side, without opening the vendor console.
When does a scheduled scan actually start running after I save its schedule?
The cron scheduler reloads its set of scheduled scans every five minutes. A new schedule_cron value saved on an existing scan takes effect within five minutes, on the next reload cycle, rather than the instant you save it. For any recurring cadence measured in hours or days this delay is invisible, but if you set a schedule moments before the intended fire time, allow up to five minutes for it to register.
What is orphan adoption and how often does it run?
Orphan adoption is the background sweep that discovers scans which exist on a vendor but have no PMAP row. Every five minutes, for each active integration, PMAP fetches the vendor’s full scan list and automatically creates rows for any scans it has not seen, as long as they are not on the blocklist. If an adopted scan is already completed, its asset import, findings import, and enrichment fire immediately. This keeps the Scans list a complete picture even for scans launched directly in a vendor console.
If I delete a scan, will orphan adoption bring it back?
No. When you delete a scan, PMAP writes its (integration_id, external_scan_id) pair to a blocklist before the delete completes. Every subsequent orphan adoption pass checks that blocklist and skips any entry on it. The vendor may keep reporting the scan, but PMAP will not re-adopt it. Deleted stays deleted.
How do scan results get imported once a scan finishes?
Import happens automatically on the completed transition. When the live sync loop writes a status of completed for a scan, or when orphan adoption discovers an already-completed scan, PMAP fires asset import, findings import, and enrichment in background goroutines. There is no separate manual import step between the scanner finishing and the findings appearing in PMAP.
What does the on-demand Refresh button do?
The Refresh button on the Scans list calls POST /sync, which fires an immediate vendor status poll and orphan adoption pass. It can be scoped to a single integration. The endpoint returns HTTP 202 immediately so the UI is never blocked, and the sync runs in the background under a detached context, so it completes even if you navigate away. It is the manual override for moments when you cannot wait for the next scheduled sweep.
Why do some completed scans briefly show zero severity counts?
Certain scans, especially those adopted before per-severity counting existed, can show zero counts even though they have findings. A completed-meta backfill sweep runs every five minutes and refreshes those counts from the actual findings in the database. The sweep is capped at twenty scans per run to avoid database pressure, so a large backlog is corrected gradually over successive ticks rather than all at once.