Configuration
August 1, 2026 ยท View on GitHub
Hawk reads hawk.toml from the workspace root by default. When that file is
absent, every binary target in the workspace becomes a production target. A
workspace without binaries must configure its audited library targets explicitly.
Use --config PATH to select a different configuration file.
Production targets
Declare every shipped binary or audited internal library with a [[production]]
entry:
[[production]]
package = "uv"
bin = "uv"
reason = "shipped package manager binary"
[[production]]
package = "uv-dev"
bin = "uv-dev"
reason = "developer binary shipped from this workspace"
[[production]]
package = "windows-helper"
bin = "windows-helper"
target = "cfg(windows)"
reason = "Windows-only binary shipped from this workspace"
[[production]]
package = "internal-api"
lib = "internal_api"
reason = "internal library consumed only within this workspace"
Each entry must specify exactly one of bin or lib. Binary targets establish
production reachability from their entry points. Library targets remain
diagnostic candidates: a public item with no workspace uses is reported as
hawk::dead_public, an item used only within its own crate is reported as
hawk::unnecessary_public, and an item used across a workspace crate boundary
retains pub. When every configured production target is a library, diagnostics
are limited to those selected library crates; other workspace crates are
compiled as consumers without becoming audit targets.
Ordinary library targets can use explicit Rust library crate types, including
crate-type = ["rlib"].
Every package and target must belong to the selected Cargo workspace. At least one production target must apply to the analyzed target.
All production targets are analyzed with the same feature profiles and
compilation target. When hawk.toml exists, its configured targets remain
authoritative; Hawk does not add other workspace binaries implicitly.
Doctest packages
By default, Hawk compiles doctests for the entire workspace. Large workspaces can restrict that pass to packages that contain doctests:
[[doctest]]
package = "uv-redacted"
When any [[doctest]] entries are present, Hawk passes only those packages to
cargo test --doc. Every configured package must belong to the selected Cargo
workspace. Omit the entries to retain the default full-workspace coverage.
Feature profiles
By default, Hawk performs one analysis with Cargo's --all-features option.
Configure a feature matrix when code that is required with a feature disabled
would otherwise be absent from that build:
[[feature-profile]]
name = "all"
all-features = true
[[feature-profile]]
name = "minimal"
no-default-features = true
[[feature-profile]]
name = "serde-only"
no-default-features = true
features = ["serde"]
Hawk compiles every production target, workspace non-production target, and selected doctest package under each profile. Fragments are stored separately for each profile, then their reachability and visibility requirements are combined before diagnostics are produced. A declaration required in any configured profile is therefore preserved.
Profile names must be unique and contain only ASCII letters, digits, -, or
_. all-features = true cannot be combined with no-default-features or an
explicit features list. A profile with none of those settings uses Cargo's
default features. Each string in features is passed as a separate Cargo
--features value.
Automatic fixes are currently rejected when multiple feature profiles are
configured. Applying a visibility change safely across several configurations
requires a coordinated fix plan; run the matrix without --fix, or select a
single profile for a fixing run. Feature profiles do not select compilation
targets; --target still selects one target for the entire analysis.
Uniform field visibility
Set preserve-uniform-field-visibility = true to retain a struct or union's
intentional uniform field visibility:
preserve-uniform-field-visibility = true
When every source-written field has the same visibility, Hawk preserves that
visibility if at least one field requires it. Otherwise, Hawk applies the least
aggressive available reduction to every field. For example, if one
pub(crate) field requires pub(super) and another can be private, Hawk
suggests pub(super) for both when hawk::unnecessary_crate_visibility is
enabled. That lint is allow-by-default; without it, Hawk preserves the group's
pub(crate) visibility. If every field can be private, Hawk reports that
reduction for the full group. The policy does not suppress hawk::dead_public.
Hawk conservatively preserves the current group when a source-written field is
absent from the compiled graph, such as a #[cfg]-disabled field, or when a
field opts out of analysis with #[allow(dead_code)]. Those fields cannot
participate in a safe group-wide fix.
This setting is disabled by default. It does not apply to mixed-visibility declarations or declarations whose complete source field list is unavailable, such as macro-generated structs.
Overrides
An override records an intentional finding without changing the analysis:
[[override]]
lint = "hawk::dead_public"
crate = "library"
item = "legacy_entry"
level = "allow"
reason = "retained temporarily while consumers migrate"
[[override]]
lint = "hawk::unnecessary_public"
crate = "library"
item = "generated_registration"
kind = "function"
level = "expect"
reason = "called by generated registration that Hawk does not model"
[[override]]
lint = "hawk::unnecessary_restricted_visibility"
crate = "library"
item = "platform::shared_helper"
level = "expect"
reason = "called by generated platform code that Hawk does not model"
[[override]]
lint = "hawk::dead_public"
crate = "platform"
item = "windows_only_api"
level = "expect"
target = "cfg(windows)"
reason = "public API retained only in the Windows build"
allow suppresses a matching finding. expect suppresses a matching finding
and reports hawk::unfulfilled_expectation if that finding is no longer
present. An override whose selectors no longer identify a compiled item
reports hawk::unknown_item. If an override without kind identifies
multiple same-named declarations, Hawk reports hawk::ambiguous_item and
suppresses none of them.
When an audit is limited to selected library targets, overrides for other
compiled workspace crates are ignored. Overrides naming an unknown crate or an
unknown item in an audited crate still report hawk::unknown_item.
Definitions from the same Cargo package with the same crate, diagnostic path,
and item kind are one logical override identity even when cfg alternatives
compile them from different source locations. An override applies to every
such physical variant, and an expect is fulfilled when at least one variant
produces the selected finding. Workspace library crate names are required to
be unique, so definitions in different packages cannot share an override
identity. Same-path declarations in different Rust namespaces remain
ambiguous unless kind is supplied.
The item value names Hawk's diagnostic path. For exported aliases, use the
alias name, such as PublicAlias; for modules, use the module path, such as
api::internal. Add kind when separate Rust namespaces define declarations
with the same path. It accepts Hawk's item-kind names, such as function,
type_alias, and constant.
Overrides filter diagnostics only. Unlike a [[production]] entry, an
override does not define a production target, establish reachability, or
preserve public visibility for referenced declarations.
Exclusions
Use an exclusion when an entire module subtree or source file is outside the diagnostic surface, for example generated code:
[[exclude]]
crate = "library"
module = "generated_bindings"
level = "expect"
reason = "generated from the protocol schema"
[[exclude]]
crate = "platform"
file = "platform/src/generated.rs"
reason = "generated platform bindings"
An exclusion must provide exactly one of module or file. A module selector
uses Hawk's diagnostic path and suppresses the selected module and all
descendants, such as generated_bindings::Message. A file selector matches
the source path printed in diagnostics. Both forms suppress all Hawk findings
in their selected scope.
Exclusions default to level = "allow". Set level = "expect" when the
selected scope should currently contain at least one finding. Hawk then reports
hawk::unfulfilled_expectation if the exclusion becomes stale because it no
longer suppresses anything.
Exclusions filter diagnostics and fixes only; they do not change reachability
or preserve visibility. Prefer an exact [[override]] when an individual
diagnostic is an intentional exception that should remain audited with
expect.
Target selectors
[[production]], [[override]], and [[exclude]] accept an optional
target. The value uses the same named targets and cfg(...) platform
expressions as Cargo target dependencies:
[[production]]
package = "windows-helper"
bin = "windows-helper"
target = "cfg(windows)"
reason = "Windows-only production binary"
[[override]]
lint = "hawk::dead_public"
crate = "platform"
item = "windows_fallback"
level = "expect"
target = "cfg(not(windows))"
reason = "non-Windows compatibility surface"
An entry is validated only when its selector applies to the analyzed compilation target. This avoids stale-expectation failures for declarations that are not compiled on that target.
External library boundaries
hawk.toml defines production targets and diagnostic exceptions. To omit an
entire workspace library crate because it exposes a supported API outside the
closed-world analysis, pass --exclude-crate:
./target/debug/cargo-hawk check \
--manifest-path /path/to/workspace/Cargo.toml \
--exclude-crate supported_library
Excluded crates are compiled as required by Cargo, but Hawk does not report their public declarations.