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

nordisk/gunnar

crates/gunnar-core/Cargo.toml

[package]
name = "gunnar-core"
description = "gunnar's repository model, identity table, ACL and path sandbox. gunnar-ssh, gunnar-grpc and gunnar-cli all sit on it."
readme = "README.md"
# 2.0.0 (2026-10-26, lane GUN-INSTALL / G-BOTH, the R5C publish): master differs from crates.io, so
# it gets its own version; a major: cargo-semver-checks names breaking changes against crates.io 1.5.0.
version = "3.0.0"
edition.workspace = true
rust-version.workspace = true
license.workspace = true
# On the `cargo install gunnar` chain (owner order 2026-10-22).
publish = true
authors.workspace = true
repository.workspace = true

[features]
default = []
# Test support: the deliberately-broken resolver and the shared escape table
# that the red-first sandbox tests consume. Enabled automatically for this
# crate's own tests through the dev-dependency below, so a server build never
# links a resolver that accepts everything.
conformance = []
# **The metered appliance's record** (`entitlement.rs`): the paid state per
# namespace, stored in the control store. Off, the module does not exist —
# the community appliance does not CONTAIN plan enforcement (the metered build
# split), and `gunnar-server/metered` is the one thing that turns this on.
metered = []
# **The txlog's test hold** (`txlog::TEST_GROUP_ENV`): read only by a test build
# that asks for it (gunnar-server's dev-dependency, for audit_read_io). Resolver 2
# keeps it out of every shipped binary.
test-hooks = []

[dependencies]
# **The directory's name rules, one copy for every product** (A34 M-6): the
# principal ids a NEW employee may take (A34-42: hashed suffixes after `-9`).
# `names` only: unicode-normalization, no network, no storage.
nornir-identity-directory = { version = "0.1.1", default-features = false, features = ["names"] }
gunnar-store = { workspace = true }
# `auth::pq_recipient_fingerprint`: an encrypted repository member's
# quantum-safe key (`age1pq1…`) is registered beside their SSH keys, and its
# format check is the crypt crate's (no cipher, `default-features = false`).
gunnar-crypt = { workspace = true, features = ["pq-keys"] }
gix-hash = { workspace = true }
ssh-key = { workspace = true }
# `auth::fingerprint_of_blob`, the ONE fingerprint. It is defined over
# the SSH wire-format key blob rather than over an `ssh-key` type, because
# `gunnar-ssh` links `ssh-key 0.7.0-rc.10` (russh 0.61 pins it exactly) while
# this crate links the workspace's `0.6`, and neither can be handed the other's
# `PublicKey`. Bytes cross that line; types do not.
#
# Neither of these is a new crate in the build graph: both already arrive under
# `ssh-key`, which computes its own fingerprints with exactly this pair. Pinned
# to the versions already resolved (sha2 0.10.9, base64ct 1.8.3), so the crate
# count is unchanged, measured with `cargo tree -p gunnar-core -e normal`.
# sha2 0.10 -> 0.11 with ssh-key 0.7 (the RustCrypto bump, 2026-10-03): still
# the pair ssh-key itself computes with.
sha2 = "0.11"
# Company SSO's rules (A34 M-4): who a verified sign-in is (claim, rebind, JIT) and the
# refusal codes, edda's one copy. `rules` alone: sha2, nothing else.
nornir-identity-sso = { version = "0.1.1", default-features = false, features = ["rules"] }
# `seal.rs`: AES-256-GCM for secrets at rest, and the system CSPRNG for
# the KEK and its nonces. NOT a new crate in the appliance graph — russh
# already links aws-lc-rs statically, measured as 123 `AWS_LC` strings in the
# stripped 15 MB appliance binary with zero PT_INTERP and zero DT_NEEDED. One
# appliance gets ONE crypto backend, which is why this is not `ring` and not a
# pure-Rust AEAD sitting beside the AES that is already there.
aws-lc-rs = { workspace = true }
# `issuer.rs` (the certificate-identity plan): the server is its own certificate issuer. Minting the
# CA, signing a CSR, and parsing an imported one. See the workspace manifest for
# why this is not `cert-helper` — openssl is banned from both halves by the dependency budget, and
# `bench/src/trust.rs` lives in a detached workspace where it is not.
rcgen = { workspace = true }
x509-parser = { workspace = true }
time = { workspace = true }
zeroize = { version = "1", default-features = false }
hex.workspace = true
# The control plane's own storage (the certificate-identity plan, `src/control.rs`). The catalog and
# the identity table were git objects behind git references until the storage
# engine landed and started refusing single-object writes; this is where they
# live now. Zero transitive dependencies.
redb = { workspace = true }
# The LEGACY Arrow IPC log (`arrow_log`) and its reader (`history`), moved here
# from `gunnar-store` on 2026-08-10. Nothing writes it: `secrets.rs` reads a
# secret out of it once, per key, to carry it into the control database, and
# `History` reads the retired reference journal. They are here rather than in
# `gunnar-store` because `gunnar-store` is the contract crate both `GitOps`
# backends dev-depend on from other repositories, and a contract crate must not
# make either of them resolve Arrow. When the legacy log is finally deleted,
# these three lines and two files go with it.
arrow-ipc = { workspace = true }
arrow-array = { workspace = true }
arrow-schema = { workspace = true }
# The IPC guard every Arrow read above goes through (`arrow_log::ipc_guard`):
# arrow-ipc panics or aborts on a hostile stream instead of returning an Err.
# default-features = false: the archive format only, no OpenZL C build. The
# server graph already links it through znippy-plugin-git.
znippy-common = { version = "0.10.5", default-features = false }
base64ct = { version = "1", features = ["alloc"] }
rand = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
anyhow = { workspace = true }
tracing = { workspace = true }

# `txlog::erase`: punch an erased frame's bytes out of the segment file
# (`fallocate(PUNCH_HOLE)`), so a deleted account's data leaves the disk. Not a
# new crate in the build graph: gunnar-server links rustix with `fs` already.
[target.'cfg(target_os = "linux")'.dependencies]
rustix = { workspace = true, features = ["fs"] }

[dev-dependencies]
# Turns on `conformance` for this crate's own test builds only.
gunnar-core = { path = ".", features = ["conformance"] }
# Same trick for `gunnar-store`: `MemGitOps` — the conformance suite's control
# backend, and the only `GitOps` implementation inside this repository — is
# behind its `conformance` feature, and `tests/catalog.rs` needs one to check
# that `point_head_at` projects the catalog's default branch onto `HEAD`. A
# server build still links no in-memory store.
gunnar-store = { workspace = true, features = ["conformance"] }
tempfile = { workspace = true }
# The encryption-key store's tests make real wraps (CRYPT-YUBI).
gunnar-crypt = { workspace = true, features = ["wrap"] }
base64 = "0.22"

[lints.rust]
unsafe_code = "forbid"