> For the complete documentation index, see [llms.txt](https://docs.layeronecloud.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.layeronecloud.com/network-monitor/scan-lifecycle.md).

# Scan lifecycle

Operations owns the durable queue for customer-requested and automatic full scans.

Operations owns the durable queue for customer-requested and automatic full scans. Monitor's existing Aegis synchronization worker is the only handoff worker: it submits released daily intents for the ownership page it already fetched, polls the queue, claims immutable request UUIDs, and forwards actual sensor execution observations and terminal results. Read-only agent plans and status pages never create work.

The Site schedule remains the source policy: 22:00 `America/New_York` by default, configurable through the existing authenticated scan settings form. The released boundary is stable in UTC until the next local daily boundary, including daylight-saving transitions. Enqueue is idempotent by allocation, assignment and boundary; Operations also suppresses overlapping active automatic jobs. The sensor retains the separate eight-automatic, two-customer and two-common host pools. Hourly common scans remain sensor-scheduled.

## Queue contract and source authority <a href="#queue-contract-and-source-authority" id="queue-contract-and-source-authority"></a>

Feature discovery requires `aegis_scan_jobs` and integer `aegis_scan_job_contract_version: 1`. Monitor uses `/api/admin/v1/aegis/scan-jobs/`:

* GET supplies `cluster_ids`, `source_site_id` and an optional UUID `after_id`. Each row includes the allocation/VM/account/cluster binding and original request window, plus `kind`, `scheduled_for`, `status` and `claimed_at`.
* POST supplies `contract_version`, `source_site_id`, `cluster_ids`, `action` and at most ten `targets`. `enqueue` targets carry the exact allocation, address, assignment and scheduled boundary. `claim` adds the immutable `request_id` instead of a scheduled boundary. `progress` adds the sensor's exact `started_at` and `observed_at` to the claim target.
* Every acknowledgment identifies the allocation and request UUID, with an `accepted`, `unchanged`, `unmatched`, `source_conflict` or `terminal` result.

Claims pin the Operations project and Monitor Site. They are not expiring execution leases and do not authorize an automatic handoff to another sensor after an ambiguous start. An acknowledged claim is still **Queued**, not Running. A lost claim response leaves a new local request unavailable to sensor plans until an idempotent retry is acknowledged. Current inventory, source mapping, assignment, connection revision and sensor authority remain mandatory; a UUID cannot be rebound to a different owner.

If Operations still advertises an active UUID after Monitor recorded its saved terminal result as delivered, an acknowledged claim for the same current assignment invalidates only that result's delivery receipt. The existing publisher retries the original result, including its UUID and observation times. Monitor does not reopen the terminal attempt, advertise it for another scan, or manufacture scanner-start proof. Failed claims and changed source, assignment or connection authority cannot trigger this recovery.

This reconciles a stale delivery receipt only while the exact terminal result is still retained. A sensor's attempted-UUID record with no pending result does not establish successful execution or delivery: a terminal payload rejection or withdrawn authorization can also retire the sensor outbox. Do not erase deduplication records or invent a completed result to repair such an unknown.

Once supported discovery is confirmed, `Site.managed_full_scans` is sticky. Plans include `managed_full_scans: true`, `scan_lifecycle_version: 1`, and the existing daily policy. Claimed queued targets receive `scan_request_id` for customer work and/or `automatic_scan_request_id` for daily work. Fresh plans stop offering a queued request after its original start deadline. Running work is reconfirmed after that deadline so its observations and final result can still be delivered, but it is not advertised as new dispatchable work. An upstream outage cannot silently reactivate the sensor's autonomous daily scheduler.

## Actual scanner-start proof <a href="#actual-scanner-start-proof" id="actual-scanner-start-proof"></a>

An established sensor POSTs lifecycle observations to the same authenticated `/agent/v1/port-scans` endpoint as final results, with exactly these fields:

```json
{
  "lifecycle_version": 1,
  "scan_id": "immutable-request-uuid",
  "address": "allocated-address",
  "capture_domain": "authorized-capture",
  "interface": "authorized-wan-owner",
  "source_address": "authorized-wan-address",
  "started_at": "actual-first-successful-spawn-RFC3339",
  "observed_at": "actual-supervised-child-observation-RFC3339"
}
```

`started_at` is the first successful Nmap spawn, never queue acceptance or the Monitor receipt time. The sensor coalesces actual heartbeat observations about every 30 seconds in a durable per-UUID outbox. The acknowledgment echoes `accepted_scan_id` and `accepted_observed_at`; it cannot clear a newer pending observation. Common scans without a managed full-scan UUID do not send this lifecycle envelope.

Monitor records the first actual start, fixed sensor/source identity, and monotonically increasing last observation. It forwards them independently of terminal result delivery under the existing worker lease. A bounded persistent cursor prevents stale, reassigned or otherwise unauthorized records from starving later healthy observations. Failed deliveries remain retryable.

Fast scans and crossed network responses can deliver the terminal result before the start observation. Matching delayed proof may fill the historical start and observation timestamps, but cannot change a terminal status or result. Its actual start must equal the terminal result's start, and its observation must not be later than completion. A final result cannot change an already observed execution's start or source identity. Existing legacy manual results that began before a new server adopted/claimed them retain their original v1 request-window validation; adoption never manufactures actual start proof.

## Completed coverage and inconclusive responses <a href="#completed-coverage-and-inconclusive-responses" id="completed-coverage-and-inconclusive-responses"></a>

`coverage_uncertain` is an optional boolean on final sensor results, defaulting to false for old payloads. A scan can finish its requested coverage while some responses remain inconclusive. Such a result may be Complete, but never retires earlier positive port evidence. The inventory explicitly labels inconclusive responses and missing ports as unverified, with original observation ages.

Operations advertises integer `aegis_scan_quality_version: 1` before Monitor sends the quality field. For older receivers, Monitor omits the unknown field and represents an uncertain Complete result as Partial. The immutable local result is not rewritten by that compatibility adaptation. Existing common/full profile negotiation and independent per-UUID result receipts still apply.

## Verification and rollout <a href="#verification-and-rollout" id="verification-and-rollout"></a>

Apply Monitor migrations 0054 and 0055 along with the matching Operations migrations, and upgrade scanner agents to the lifecycle-capable build. Older agents cannot report actual-start observations or consume automatic queue IDs; do not interpret their mere service activity as proof that a managed job is Running. Never clear a running request or create a replacement UUID to make its status look healthy after an ambiguous failure.

Monitor regression coverage is in `test_scan_jobs.py`, the existing scan and sender tests, and `applications_ui.test.js`. It covers lost claim responses, source/revision rejection, terminal-first start proof, immutable execution identity, running work past its start deadline, progress fairness, old receiver quality adaptation and preservation of prior positive evidence.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.layeronecloud.com/network-monitor/scan-lifecycle.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
