id: dependencies
title: What atgc ships is known, licensed, and not advised against
status: open
repos: [atgc]
dependsOn: []
exitCriterion: >
cargo-deny passes with no ignored advisory that has a fix atgc could take. #
dependencies #
cargo deny check runs and passes, and the file records why each ignore is
there and what would remove it — visible rather than silenced, and cargo-deny
reports advisory-not-detected once an ignored id stops matching, so no ignore
can outlive its advisory unnoticed.
Two advisories stand, both under jacquard and neither fixable here, and the
open entries below are mostly the record of not fixing them: the routes that
look available (a vendored fork, dropping the dns feature, a
[patch.crates-io] that is a silent no-op) are each worse than the advisory,
and re-deriving that is a day's work every time.
Three bugs found in jacquard are recorded here too, unreported. They are the
reason vendor/jacquard-oauth exists, and landing them upstream is the same
work as deleting it.
What it needs #
No table crate, surveyed while sizing the entry above and recorded so
it is not surveyed twice. comfy-table and tabled both draw a
table — borders, headers, a layout engine — where every listing here
prints bare space-separated columns in the gh idiom, so either one
would be fought to make it render less; comfy-table is ANSI-aware
only behind custom_styling, which pulls console, and tabled's
width machinery is a dependency tree of its own. tabwriter is the
right size and solves the wrong problem: elastic tabstops size a
column to its content, and these widths are fixed on purpose so that
two invocations line up. What is wanted is one term::width and one
pad_to beside it — the entry above, and about forty lines
Two RustSec advisories, both under jacquard and neither fixable here.
hickory-proto 0.24.4 (RUSTSEC-2026-0119, O(n²) name compression, CPU
exhaustion) needs jacquard-identity to move off hickory-resolver 0.24 —
a semver-incompatible bump cargo update cannot make. rsa 0.9.10
(RUSTSEC-2023-0071, Marvin timing sidechannel) has no fixed version at
all; it arrives through jose-jwk, for a key type atgc's ES256 DPoP keys
never exercise. Both still stood on 2026-08-15, confirmed independently
by cargo audit and cargo-deny 0.20.2 against the same lockfile, and
both are now in deny.toml's advisories.ignore with the removal
condition for each — visible rather than silenced, and cargo-deny
reports advisory-not-detected once an ignored id stops matching, so
neither can outlive its advisory unnoticed. cargo audit adds a third
that cargo-deny does not: atomic-polyfill 1.0.3 (RUSTSEC-2023-0089,
unmaintained), four levels down through heapless <- postcard <-
jacquard-common. That difference is deny.toml's unmaintained = "workspace" working as intended, not a disagreement about facts
RUSTSEC-2026-0119 rechecked on 2026-08-17, and the answer is still no.
The blocker is one line upstream: jacquard-identity 0.12.1 declares
hickory-resolver = "^0.24", and 0.12.1 is the newest release of every
jacquard crate — the crates.io index lists nothing above it for
jacquard, -identity, -oauth or -common, and the repo's last commits are
a month old and unrelated to DNS. 0.24.4 is the end of the 0.24 line,
there is no backport, and the advisory's patched list is exactly
>= 0.26.1. So cargo update -p hickory-proto locks 0 packages, and
--precise 0.26.1 (or 0.25.2, on either hickory crate) fails outright
quoting the ^0.24 requirement. The part worth writing down is that
[patch.crates-io] aimed at hickory-resolver 0.26.1 is not an error
but a silent no-op : cargo printed no warning at all and
cargo tree -i hickory-proto still showed 0.24.4. Anyone who tries
that and does not re-run cargo audit will believe they fixed it.
Fixable when jacquard-identity requires hickory-resolver >= 0.26.1 and
jacquard depends on that release. No upstream issue tracks it — ten are
open and none mention hickory, DNS or RustSec — so the only thing to
watch is the version, and filing one upstream would be worth more than
another recheck here
Two real routes to RUSTSEC-2026-0119, both rejected on 2026-08-17 and
recorded so they are not rediscovered as bright ideas. Vendoring a
forked hickory-proto still numbered 0.24.4 would satisfy cargo, and the
upstream fix is genuinely small — COMPRESSION_CANDIDATE_LIMIT = 64 in
serialize/binary/encoder.rs, COMPRESSED_NAME_LIMIT = 120 in
rr/domain/name.rs — but it lands in an API rewritten between 0.24 and
0.26 (no_std alloc, ProtoErrorKind -> ProtoError, EncodeMode ->
NameEncoding), so it means hand-porting into a 46k-line DNS
wire-format crate this repo would then own, and a path-sourced fork is
invisible to cargo audit — silencing the tool rather than fixing the
code. The other route is dropping jacquard's dns feature, which drops
hickory outright; jacquard-identity then resolves _atproto.<handle>
TXT over DNS-over-HTTPS against a hardcoded cloudflare-dns.com. That
trades a DoS advisory for sending every handle lookup to one third
party and ignoring the system resolver, which is the wrong trade for an
ATProto CLI. Weighing against both: hickory-resolver 0.24.4 has no
non-test site that re-encodes a received message — DnsResponse keeps
the wire buffer it was handed — so the only BinEncoder run on atgc's
path is the single-question query it builds itself, which is not the
many-records encode the advisory needs. That lowers the practical
exposure; it does not clear the advisory, so the ignore stays
Drop the vendored jacquard-oauth once the rpc ?aud=* serialization
fix lands upstream; report the bug (Cargo.toml [patch] has details)
Surveyed and deliberately left alone, recorded so nobody re-litigates
them: the two percent-encoders (not inverses, and documented at length
as to why), the four base64/base32/CID hand-rolls, config/lock.rs on
std's File::try_lock, the thirty-line word wrapper, a retry/backoff
crate, rand/uuid for the invocation id, strsim, shell-quoting and
glob crates, and the sixty-line loopback HTTP server.
chrono was on this list and has been taken off it deliberately, not
by oversight. The rejection was made against a cost that is not paid:
jacquard-common holds a chrono::DateTime<FixedOffset> inside every
Datetime, with the same feature set, so chrono is compiled into every
build of atgc regardless and declaring it adds no crate and no compile
time. What it buys is term::column::day printing a real UTC calendar
date instead of ten characters off the front of somebody else's local
stamp — a behaviour fix, and one with no cheaper spelling: day takes
a raw &str at every call site, and reaching through
jacquard::common::deps::chrono instead would couple atgc to another
crate's re-export surface, which it is free to withdraw
Report upstream to jacquard: client_id on a token request comes from
the caller's ClientData rather than from the session, and
ClientSessionData has no field to hold it, so no loopback client can
refresh a grant across process boundaries. oauth/loopback.rs's own
helper looks to have the same bug. Not patched in vendor/ on
purpose — see the note beside the patch in Cargo.toml
Report upstream to jacquard, second item: JacquardResolver takes no
DNS resolver. new_dns and with_system_dns both build hickory with
ResolverConfig::default(), which is Google's 8.8.8.8 — the latter's
name says otherwise, and hickory's own from_system_conf is what it
sounds like. The field is private, so a caller cannot supply one, and
atgc now sends handle lookups to Google rather than to the resolver the
machine is configured with. An Option<Arc<TokioAsyncResolver>>
argument would settle it. Wanted before anyone points atgc at a PDS on
a network with split-horizon DNS
Report upstream to jacquard, third item: the two places a
server-returned scope string is parsed — callback_core and
ClientSessionData::update_with_tokens — did it with
Scopes::new().expect(), so an authorization server extending the
scope vocabulary aborts every client built on the crate. Patched in
vendor/ (unlike the second item) because the failure is not
hypothetical and lands mid-callback. The lossy parser is the shape to
offer upstream; whether it should also report what it dropped, rather
than leaving each caller to re-derive it from the raw string as atgc
does, is the part worth asking about
Done #
cargo deny check runs, and passes. cargo-deny 0.20.2 (prebuilt Linux
binary in ~/.local/bin, not a source build) parsed deny.toml on
2026-08-15 without complaint, so the version guesses in its header held.
Three findings, all now answered in the file: the two advisories above;
tangled-lexicon and userinput-lexicon counted as unlicensed
third-party code, since both are generated here and carry no license
key ([licenses.private] ignore = true, cargo-deny's own answer for an
unpublished workspace member); and the wildcard finding below. The
license allow list needed no change at all — no missing entry, no
unused one. Ten multiple-versions warnings, as expected
h2 and chacha20 bumped on 2026-09-05: cargo update -p h2 -p chacha20 moved 0.4.15 to 0.4.19 and 0.10.1 to 0.10.2, clearing
RUSTSEC-2026-0258 (h2's unbounded empty DATA frames) and the yanked
chacha20 release, neither of which is one of the two advisories above.
The gap that let a failing lockfile sit on main unnoticed is closed
too: cargo deny check advisories now runs as a prek hook, scoped to
Cargo.lock and deny.toml, so the next drift is refused at the
commit that introduces it