This project is a Rust CLI + MCP server for package safety checks.
- Install stable Rust toolchain.
- Clone the repo.
- Enable the pre-commit hook (one-time):
git config core.hooksPath .githooks- Run:
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo testThe pre-commit hook runs cargo fmt and cargo clippy automatically before each commit.
- Do not write to stdout from MCP server logic (
src/mcp/server.rs) except tool responses. - Prefer explicit failure over silent fallback when checks/registry calls fail.
- Keep changes focused; avoid unrelated refactors in feature PRs.
When adding or updating registry/advisory HTTP calls, use safe-pkgs-registry-http (crate path:
crates/http) instead of
open-coding per-crate reqwest request/retry/error logic.
Use:
build_http_client()for a preconfigured client (timeouts + user-agent).send_with_retry(...)for retry/backoff and429/Retry-Afterhandling.map_status_error(...)for consistent status ->RegistryError::Transport.parse_json(...)for consistent JSON parse ->RegistryError::InvalidResponse.
Avoid:
- direct
.send().await+ custom retry loops in registry crates - ad-hoc user-agent headers per request
- hand-written status/error-mapping strings duplicated across crates
Default user-agent is safe-pkgs/<version>. Override only via SAFE_PKGS_HTTP_USER_AGENT when needed.
Use the same shape as existing registry crates:
crates/registry/<name>/
Cargo.toml
src/
lib.rs
registry.rs
lockfile.rs
registry.rs: implementRegistryClient.lockfile.rs: implementLockfileParserif lockfile auditing is supported; otherwise return no parser.lib.rs: export client/parser types and implementregistry_definition() -> RegistryDefinition.
In your crate src/lib.rs, implement registry_definition() using safe_pkgs_core::RegistryDefinition:
key(string used by MCP/CLI)create_client- optional
create_lockfile_parser
- Add crate-level tests (
registry.rsandlockfile.rsas needed).
- If lockfile/manifests are supported, validate parser behavior.
- If not, ensure parser is absent (
None) inregistry_definition().
- Add member in root
Cargo.toml[workspace].members. - Add dependency in root
Cargo.toml[dependencies].
Update app_registry_definitions() in src/main.rs to include your crate's registry_definition().
If your registry cannot support specific checks, set excluded_checks on the RegistryDefinition returned by your crate's registry_definition():
RegistryDefinition {
key: "myregistry",
create_client,
create_lockfile_parser: Some(create_lockfile_parser),
excluded_checks: &["install_script"],
}No changes to src/main.rs or any other crate are required.
Checks are separate crates under crates/checks/, and the app enables them via a single list in src/main.rs.
Use this shape:
crates/checks/<name>/
Cargo.toml
src/
lib.rs
In your crate src/lib.rs:
- Define your check type and ID constant.
- Implement
safe_pkgs_core::Checkwith:id()description()- optional
priority()(lower runs earlier, default100) - optional
runs_on_missing_package()/runs_on_missing_version() - optional
needs_weekly_downloads()/needs_advisories() run(&CheckExecutionContext) -> Result<Vec<CheckFinding>, RegistryError>
- Export
pub fn create_check() -> Box<dyn Check>.
- Add unit tests in the check crate (
src/lib.rswith#[cfg(test)]ortests/).
- Validate missing-data behavior for your check (
missing package,missing version, and normal path where relevant). - Run tests before enabling the check in app wiring.
- Add dependency in root
Cargo.toml[dependencies]. Note: workspace member globcrates/checks/*is already enabled.
- Update
app_check_factories()insrc/main.rsto includesafe_pkgs_check_<name>::create_check.
If a registry cannot support your new check, add the check ID to excluded_checks in that registry crate's registry_definition().
If no override is needed, no registry changes are required.
Run:
cargo run -- support-mapConfirm the new check appears with expected support and runtime requirement flags.
If your change affects checks, registries, or support policy, update the docs support map:
docs/assets/check-support-map.svg(embedded on front page)
Use cargo run -- support-map as the runtime source of truth while updating the SVG.
Run all:
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo testIf your change affects docs, also preview/update docs under docs/ and zensical.toml (built with Zensical).