Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

agent/lib/Cargo.toml

[package]
name = "holger-agent-lib"
# 0.6.10: local source DRIFTED from the published 0.6.9 under the same version number.
# crates.io bytes are immutable, so the edit only reaches consumers as a new
# version. Bumped 2026-08-23, second wave of the release cascade.
version.workspace = true
edition = "2021"
authors = ["Ignalina ApS", "Vetra Automation AB"]
description = "Holger agent library: airgap, export, push operations using connectors"
license = "AGPL-3.0-only"
repository = "https://git.gunnar.rs/nordisk/holger"
# Development notes, agent instructions and machine-local tooling stay in the
# repository; they are not part of the published crate.
exclude = [
    ".nornir/",
]

[dependencies]
traits = { package = "holger-traits", version = "3.0.0", path = "../../traits" }

# The network connectors — the ONLY reqwest carriers in the agent closure. Each
# links `reqwest` unconditionally, so they are OPTIONAL and gated behind the
# `online` feature: the offline agent (archive↔directory↔archive conversions over
# the generic `ConnectorTrait` seam) needs none of them, and dropping them drops
# reqwest entirely — the zero-egress airgap floor. `default = ["online"]` keeps the
# existing consumers (agent-cli default build, server-ui `tools`, bench-scenarios)
# byte-identical; only a `--no-default-features` agent build omits them.
connector-nexus = { package = "holger-nexus-connector", version = "3.0.0", path = "../connector/nexus", optional = true }

connector-artifactory = { package = "holger-artifactory-connector", version = "3.0.0", path = "../connector/artifactory", optional = true }

connector-world = { package = "holger-world-connector", version = "3.0.0", path = "../connector/world", optional = true }

# nornir-vault (edda) — WHERE A MIRROR'S UPSTREAM CREDENTIAL LIVES. A continuous
# mirror must re-authenticate after a restart, so its username/password cannot
# stay a CLI argument that is kept nowhere; `src/creds.rs` reads it from the
# at-rest-encrypted local vault edda already ships (envelope encryption, 0600
# file, no `Serialize`/`Display`/revealing `Debug` on any secret type). It is
# REUSED, not re-implemented, and it is OPTIONAL and pinned to `online` for the
# same reason the connectors are: the airgap tiers (`embedded`, `mini`) link no
# network connector, therefore no upstream, therefore no upstream credential —
# this does not widen the zero-egress floor by a single crate.
# NOTE the sibling checkout is 0.1.2 while the registry is 0.1.0; the `version`
# req stays at "0.1" so publishing resolves, and the path wins locally.
nornir-vault = { version = "0.2.0", optional = true }
# RON is the config language for the credential references (`ron_*` fields, same
# as the server's repository graph) and for the vault entry's own plaintext.
# Owner's rule: new config is RON, never an environment variable.
ron = { version = "0.12", optional = true }
# Zeroize-on-drop for the transient decode buffer between the vault and a
# `SecretString`, so the decode leaves no plaintext copy behind.
zeroize = { version = "1", features = ["derive"], optional = true }


anyhow = "1.0.104"
# async-trait: was a dev-dependency only (the fake ConnectorTrait impls in the
# strict-transfer tests). `migrate::ConnectorSink` implements the real
# `traits::migrate::MigrationSink` in the LIBRARY, so the macro has to be here.
# Same crate, same version the workspace already resolves — no new entry in
# Cargo.lock, only a dev-only edge becoming a normal one.
async-trait = "0.1"
tokio = { version = "1", features = ["full"] }
# reqwest is not called directly here — the connectors above are its carriers — but
# it is declared OPTIONAL and pinned to the `online` feature so an offline build
# links no outbound HTTP client even by an accidental edge.
reqwest = { version = "0.13.5", features = ["rustls", "json"], optional = true }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
znippy-compress = { version = "0.10.0" }
crossbeam-channel = "0.5"
znippy-common = { version = "0.10.4", features = ["host-decompressors"] }
# gatling::io — async sibling of the sync gatling engine: a bounded, no-barrier
# in-flight pool for latency-bound network I/O (the SAVE-stage uploads).
# ONE copy of the engine, not two. This line carried `path =` as well as
# `version =`, so cargo resolved the sibling checkout AND crates.io pulled its own
# through `znippy-common` -> `lgz`. MEASURED 2026-09-20, `cargo tree -e normal`
# from `server/cli`: `znippy-zoomies` 0.1.24 (registry) beside 0.1.25 (path),
# `rotaryengine` 0.1.10 beside 0.1.12, `lgz` 0.3.14 twice, `ldeflate` 0.1.1 beside
# 0.1.2 -- four engines compiled into every binary, two of each. Registry-only
# collapses all four pairs; `gatling::io::run`, the only API this crate uses, is
# identical in both.
znippy-zoomies = { version = "0.1.26" }
log = "0.4"
toml = "1.1.8"
tempfile = "3"
tar = "0.4"
zstd = "0.14"
# Strict-transfer receipts: per-asset dual digests + UTC timestamps.
sha2 = "0.11"
blake3 = "1"
chrono = { version = "0.4", features = ["serde", "clock"] }

[features]
# `online` — the network connectors (Nexus / Artifactory / crates.io·PyPI `world`)
# and their `reqwest` closure: the OUTBOUND transfer capability that distinguishes
# the dev agent from the offline airgap floor. ON by default so existing consumers
# are byte-for-byte unchanged; an `online`-off (`--no-default-features`) build links
# NO connector and NO reqwest — only the generic `ConnectorTrait` archive/directory
# operations remain, which is the zero-egress micro agent.
default = ["online"]
online = [
    "dep:connector-nexus",
    "dep:connector-artifactory",
    "dep:connector-world",
    "dep:reqwest",
    # The upstream-credential vault rides the SAME gate: a credential only exists
    # because an upstream exists. `--no-default-features` links no vault.
    "dep:nornir-vault",
    "dep:ron",
    "dep:zeroize",
]

# `vault-os-keyring` — unlock the credential vault from the OS keyring (Secret
# Service / libsecret / KDE Wallet) instead of an operator PIN. OFF by default:
# it adds the `keyring` crate and needs a running Secret Service, so a headless
# mirror host must not be made to link it. With it off, a config that names
# `OsKeyring` is REFUSED at boot with the rebuild hint rather than silently
# falling back to something weaker.
vault-os-keyring = ["nornir-vault/os-keyring"]

[dev-dependencies]
tempfile = "3"