# How a scan works

1. **Read.** licenseal finds the manifests and lockfiles under the scanned directory: see [What is scanned](<https://licenseal.shcherbak.ai/concepts/discovery/index.md>). The files it reads are listed for each [ecosystem](<https://licenseal.shcherbak.ai/ecosystems/index.md>). It runs no package manager.
2. **Resolve.** A lockfile gives the dependency tree as it is installed. Where no lockfile covers a dependency, licenseal walks the registry and marks the versions it picks as estimates. See [Dependency resolution](<https://licenseal.shcherbak.ai/concepts/dependency-resolution/index.md>) and [Versions and estimates](<https://licenseal.shcherbak.ai/concepts/version-resolution/index.md>). A dependency that comes from a git repository, a URL, or a path is an [off-registry dependency](<https://licenseal.shcherbak.ai/concepts/off-registry/index.md>).
3. **Classify.** A package's license is read from public registry metadata, or from `composer.lock` for a locked PHP package. It is read as a [license expression](<https://licenseal.shcherbak.ai/concepts/license-expressions/index.md>), given a risk level, and compared with the [project license](<https://licenseal.shcherbak.ai/concepts/project-license/index.md>). See [How licenseal decides](<https://licenseal.shcherbak.ai/concepts/verdicts/index.md>).
4. **Report.** The result is a table, a Markdown file, or JSON, and an exit code that CI can gate on. See [What fails a check](<https://licenseal.shcherbak.ai/concepts/check-failures/index.md>).

Nothing is installed or built, and no package archive is downloaded. Every host that is contacted is listed under [Network egress](<https://licenseal.shcherbak.ai/security/network/index.md>), and what is sent to it under [What leaves the process](<https://licenseal.shcherbak.ai/security/data-sent/index.md>).
