diff --git a/Makefile b/Makefile
index e57b2ba74..ff96de692 100644
--- a/Makefile
+++ b/Makefile
@@ -123,7 +123,7 @@ validate:
 	(cd crates/ostree-ext && cargo check --no-default-features)
 	(cd crates/lib && cargo check --no-default-features)
 	cargo clippy -- $(CLIPPY_CONFIG)
-	env RUSTDOCFLAGS='-D warnings' cargo doc --lib
+	env RUSTDOCFLAGS='-D warnings' cargo doc --workspace --no-deps --document-private-items
 .PHONY: validate
 fix-rust:
 	cargo clippy --fix --allow-dirty -- $(CLIPPY_CONFIG)
diff --git a/crates/ostree-ext/src/chunking.rs b/crates/ostree-ext/src/chunking.rs
index e6fca18b8..0a60296d5 100644
--- a/crates/ostree-ext/src/chunking.rs
+++ b/crates/ostree-ext/src/chunking.rs
@@ -600,8 +600,8 @@ fn get_partitions_with_threshold<'a>(
 ///  Compare the flattened vector to components to see if pkgs added, updated,
 ///  removed or kept same
 ///  if pkgs added, then add them to the last bin of prior
-///  if pkgs removed, then remove them from the prior[i]
-///  iterate through prior[i] and make bins according to the name in nevra of pkgs to update
+///  if pkgs removed, then remove them from the prior\[i\]
+///  iterate through prior\[i\] and make bins according to the name in nevra of pkgs to update
 ///  required packages
 /// else if pkg structure to be changed || prior build not specified
 ///  Recompute optimal packaging structure (Compute partitions, place packages and optimize build)
diff --git a/crates/ostree-ext/src/cli.rs b/crates/ostree-ext/src/cli.rs
index 39553e740..b386c09b9 100644
--- a/crates/ostree-ext/src/cli.rs
+++ b/crates/ostree-ext/src/cli.rs
@@ -152,7 +152,7 @@ pub(crate) enum ContainerOpts {
         authfile: Option<PathBuf>,
 
         /// Path to a JSON-formatted serialized container configuration; this is the
-        /// `config` property of https://github.com/opencontainers/image-spec/blob/main/config.md
+        /// `config` property of <https://github.com/opencontainers/image-spec/blob/main/config.md>
         #[clap(long)]
         config: Option<Utf8PathBuf>,
 
diff --git a/crates/ostree-ext/src/container/encapsulate.rs b/crates/ostree-ext/src/container/encapsulate.rs
index c4672cf92..c012797ab 100644
--- a/crates/ostree-ext/src/container/encapsulate.rs
+++ b/crates/ostree-ext/src/container/encapsulate.rs
@@ -32,7 +32,7 @@ pub const DIFFID_LABEL: &str = "ostree.final-diffid";
 pub const BOOTC_LABEL: &str = "containers.bootc";
 
 /// Annotation injected into the layer to say that this is an ostree commit.
-/// However, because this gets lost when converted to D2S2 https://docs.docker.com/registry/spec/manifest-v2-2/
+/// However, because this gets lost when converted to D2S2 <https://docs.docker.com/registry/spec/manifest-v2-2/>
 /// schema, it's not actually useful today.  But, we keep it
 /// out of principle.
 const BLOB_OSTREE_ANNOTATION: &str = "ostree.encapsulated";
diff --git a/crates/ostree-ext/src/container/store.rs b/crates/ostree-ext/src/container/store.rs
index a2c639f39..ea0665d40 100644
--- a/crates/ostree-ext/src/container/store.rs
+++ b/crates/ostree-ext/src/container/store.rs
@@ -122,7 +122,7 @@
 //!
 //! ## See Also
 //!
-//! - [`super::encapsulate`]: Export ostree commits to container images
+//! - [`fn@super::encapsulate`]: Export ostree commits to container images
 //! - [`crate::tar`]: Tar stream format for layer content
 
 use super::*;
@@ -183,7 +183,7 @@ pub type MetaFilteredData = HashMap<String, HashMap<String, u32>>;
 
 /// The ref prefixes which point to ostree deployments.  (TODO: Add an official API for this)
 const OSTREE_BASE_DEPLOYMENT_REFS: &[&str] = &["ostree/0", "ostree/1"];
-/// A layering violation we'll carry for a bit to band-aid over https://github.com/coreos/rpm-ostree/issues/4185
+/// A layering violation we'll carry for a bit to band-aid over <https://github.com/coreos/rpm-ostree/issues/4185>
 const RPMOSTREE_BASE_REFS: &[&str] = &["rpmostree/base"];
 
 /// Convert e.g. sha256:12345... into `/ostree/container/blob/sha256_2B12345...`.
@@ -504,7 +504,7 @@ pub fn manifest_digest_from_commit(commit: &glib::Variant) -> Result<Digest> {
 
 /// Given a target diffid, return its corresponding layer.  In our current model,
 /// we require a 1-to-1 mapping between the two up until the ostree level.
-/// For a bit more information on this, see https://github.com/opencontainers/image-spec/blob/main/config.md
+/// For a bit more information on this, see <https://github.com/opencontainers/image-spec/blob/main/config.md>
 fn layer_from_diffid<'a>(
     manifest: &'a ImageManifest,
     config: &ImageConfiguration,
@@ -608,7 +608,7 @@ fn timestamp_of_manifest_or_config(
 }
 
 /// Automatically clean up files that may have been injected by container
-/// builds. xref https://github.com/containers/buildah/issues/4242
+/// builds. xref <https://github.com/containers/buildah/issues/4242>
 fn cleanup_root(root: &Dir) -> Result<()> {
     const RUNTIME_INJECTED: &[&str] = &["usr/etc/hostname", "usr/etc/resolv.conf"];
     for ent in RUNTIME_INJECTED {
diff --git a/crates/ostree-ext/src/container/unencapsulate.rs b/crates/ostree-ext/src/container/unencapsulate.rs
index c386576d0..bbcbaac4d 100644
--- a/crates/ostree-ext/src/container/unencapsulate.rs
+++ b/crates/ostree-ext/src/container/unencapsulate.rs
@@ -185,7 +185,7 @@ pub async fn unencapsulate(repo: &ostree::Repo, imgref: &OstreeImageReference) -
     importer.unencapsulate().await
 }
 
-/// A wrapper for [`get_blob`] which fetches a layer and decompresses it.
+/// A wrapper for [`ImageProxy::get_blob`] which fetches a layer and decompresses it.
 pub(crate) async fn fetch_layer<'a>(
     proxy: &'a ImageProxy,
     img: &OpenedImage,
diff --git a/crates/ostree-ext/src/globals.rs b/crates/ostree-ext/src/globals.rs
index 234cb5ef2..4b852f659 100644
--- a/crates/ostree-ext/src/globals.rs
+++ b/crates/ostree-ext/src/globals.rs
@@ -18,7 +18,7 @@ struct ConfigPaths {
 
 /// Get the runtime and persistent config directories.  In the system (root) case, these
 /// system(root) case:  /run/ostree           /etc/ostree        /usr/lib/ostree
-/// user(nonroot) case: /run/user/$uid/ostree ~/.config/ostree   <none>
+/// user(nonroot) case: /run/user/$uid/ostree ~/.config/ostree   (none)
 fn get_config_paths(root: bool) -> &'static ConfigPaths {
     if root {
         static PATHS_ROOT: OnceLock<ConfigPaths> = OnceLock::new();
diff --git a/crates/ostree-ext/src/ostree_manual.rs b/crates/ostree-ext/src/ostree_manual.rs
index 26a122109..6c8110388 100644
--- a/crates/ostree-ext/src/ostree_manual.rs
+++ b/crates/ostree-ext/src/ostree_manual.rs
@@ -6,7 +6,7 @@ use std::ptr;
 use ostree::prelude::{Cast, InputStreamExtManual};
 use ostree::{gio, glib};
 
-/// Equivalent of `g_file_read()` for ostree::RepoFile to work around https://github.com/ostreedev/ostree/issues/2703
+/// Equivalent of `g_file_read()` for ostree::RepoFile to work around <https://github.com/ostreedev/ostree/issues/2703>
 #[allow(unsafe_code)]
 pub fn repo_file_read(f: &ostree::RepoFile) -> Result<gio::InputStream, glib::Error> {
     use glib::translate::*;
diff --git a/crates/ostree-ext/src/sysroot.rs b/crates/ostree-ext/src/sysroot.rs
index f2a1ae3eb..42c398eea 100644
--- a/crates/ostree-ext/src/sysroot.rs
+++ b/crates/ostree-ext/src/sysroot.rs
@@ -102,7 +102,7 @@ pub fn list_stateroots(sysroot: &ostree::Sysroot) -> Result<Vec<Stateroot>> {
     Ok(r)
 }
 
-/// Given a string, if it matches the form of an automatic state root, parse it into its <year>.<serial> pair.
+/// Given a string, if it matches the form of an automatic state root, parse it into its `<year>.<serial>` pair.
 fn parse_auto_stateroot_name(name: &str) -> Option<(u64, u64)> {
     let Some(statename) = name.strip_prefix(AUTO_STATEROOT_PREFIX) else {
         return None;
diff --git a/crates/ostree-ext/src/tar/export.rs b/crates/ostree-ext/src/tar/export.rs
index 196f472e7..375554e64 100644
--- a/crates/ostree-ext/src/tar/export.rs
+++ b/crates/ostree-ext/src/tar/export.rs
@@ -378,7 +378,7 @@ impl<'a, W: std::io::Write> OstreeTarWriter<'a, W> {
     /// Append all xattrs to the tar stream *except* security.selinux, because
     /// that one doesn't become visible in `podman run` anyways, so we couldn't
     /// rely on it in some cases.
-    /// https://github.com/containers/storage/blob/0d4a8d2aaf293c9f0464b888d932ab5147a284b9/pkg/archive/archive.go#L85
+    /// <https://github.com/containers/storage/blob/0d4a8d2aaf293c9f0464b888d932ab5147a284b9/pkg/archive/archive.go#L85>
     #[context("Writing tar xattrs")]
     fn append_tarstream_xattrs(&mut self, xattrs: &glib::Variant) -> Result<()> {
         let v = xattrs.data_as_bytes();
@@ -646,7 +646,7 @@ impl<'a, W: std::io::Write> OstreeTarWriter<'a, W> {
 
 /// Append xattrs to the tar stream as PAX extensions, excluding security.selinux
 /// which doesn't become visible in container runtimes anyway.
-/// https://github.com/containers/storage/blob/0d4a8d2aaf293c9f0464b888d932ab5147a284b9/pkg/archive/archive.go#L85
+/// <https://github.com/containers/storage/blob/0d4a8d2aaf293c9f0464b888d932ab5147a284b9/pkg/archive/archive.go#L85>
 #[context("Writing tar xattrs")]
 fn append_pax_xattrs<W: std::io::Write>(
     out: &mut tar::Builder<W>,
diff --git a/crates/ostree-ext/src/tokio_util.rs b/crates/ostree-ext/src/tokio_util.rs
index e21b142c2..acc72fb30 100644
--- a/crates/ostree-ext/src/tokio_util.rs
+++ b/crates/ostree-ext/src/tokio_util.rs
@@ -52,8 +52,8 @@ where
     })
 }
 
-/// Flatten a nested Result<Result<T>>, defaulting to converting the error type to an `anyhow::Error`.
-/// See https://doc.rust-lang.org/std/result/enum.Result.html#method.flatten
+/// Flatten a nested `Result<Result<T>>`, defaulting to converting the error type to an `anyhow::Error`.
+/// See <https://doc.rust-lang.org/std/result/enum.Result.html#method.flatten>
 pub(crate) fn flatten_anyhow<T, E>(r: std::result::Result<Result<T>, E>) -> Result<T>
 where
     E: Display + Debug + Send + Sync + 'static,
diff --git a/crates/system-reinstall-bootc/src/prompt.rs b/crates/system-reinstall-bootc/src/prompt.rs
index ba769f902..ae7f5b875 100644
--- a/crates/system-reinstall-bootc/src/prompt.rs
+++ b/crates/system-reinstall-bootc/src/prompt.rs
@@ -82,7 +82,7 @@ pub(crate) fn reboot() -> Result<()> {
 }
 
 /// Temporary safety mechanism to stop devs from running it on their dev machine. TODO: Discuss
-/// final prompting UX in https://github.com/bootc-dev/bootc/discussions/1060
+/// final prompting UX in <https://github.com/bootc-dev/bootc/discussions/1060>
 #[context("temporary_developer_protection_prompt")]
 pub(crate) fn temporary_developer_protection_prompt() -> Result<()> {
     // Print an empty line so that the warning stands out from the rest of the output
diff --git a/crates/system-reinstall-bootc/src/users.rs b/crates/system-reinstall-bootc/src/users.rs
index de08d3b1b..b10417426 100644
--- a/crates/system-reinstall-bootc/src/users.rs
+++ b/crates/system-reinstall-bootc/src/users.rs
@@ -25,7 +25,7 @@ fn loginctl_users() -> Result<BTreeSet<String>> {
     loginctl_parse(loginctl_raw_output)
 }
 
-/// See [`test::test_parse_lsblk`] for example loginctl output
+/// See `test::test_parse_lsblk` for example loginctl output
 #[context("loginctl_parse")]
 fn loginctl_parse(users: Value) -> Result<BTreeSet<String>> {
     users
diff --git a/crates/utils/src/path.rs b/crates/utils/src/path.rs
index 5409fecd9..5c3f8f08f 100644
--- a/crates/utils/src/path.rs
+++ b/crates/utils/src/path.rs
@@ -14,7 +14,7 @@ pub struct PathQuotedDisplay<'a> {
 /// characters such as '+' which are typically safe, but it's fine if
 /// we're overly conservative.
 ///
-/// For bash for example: https://www.gnu.org/software/bash/manual/html_node/Definitions.html#index-metacharacter
+/// For bash for example: <https://www.gnu.org/software/bash/manual/html_node/Definitions.html#index-metacharacter>
 fn is_shellsafe(c: char) -> bool {
     matches!(c, '/' | '.' | '-' | '_' | ',' | '=' | ':') || c.is_alphanumeric()
 }
diff --git a/crates/xtask/src/bcvk.rs b/crates/xtask/src/bcvk.rs
index e46dcb8f7..7d883d205 100644
--- a/crates/xtask/src/bcvk.rs
+++ b/crates/xtask/src/bcvk.rs
@@ -93,7 +93,7 @@ impl BcvkInstallOpts {
     /// Return firmware / secure-boot args for `bcvk libvirt run`.
     ///
     /// For sealed images the secure boot keys directory must already
-    /// exist; the caller can use [`ensure_secureboot_keys`] first.
+    /// exist; the caller can use `ensure_secureboot_keys` first.
     #[context("Building firmware arguments")]
     pub(crate) fn firmware_args(&self) -> Result<Vec<String>> {
         let sb_keys_dir = Utf8Path::new(DEFAULT_SB_KEYS_DIR);
diff --git a/crates/xtask/src/tmt.rs b/crates/xtask/src/tmt.rs
index c9a87dec5..0152285ef 100644
--- a/crates/xtask/src/tmt.rs
+++ b/crates/xtask/src/tmt.rs
@@ -814,7 +814,7 @@ pub(crate) fn tmt_provision(sh: &Shell, args: &TmtProvisionArgs) -> Result<()> {
 /// # extra:
 /// #   try_bind_storage: true
 /// # tmt:
-/// #   <yaml content>
+/// #   (yaml content)
 fn parse_tmt_metadata(content: &str) -> Result<Option<TmtMetadata>> {
     let mut number = None;
     let mut in_extra_block = false;
diff --git a/crates/xtask/src/xtask.rs b/crates/xtask/src/xtask.rs
index 38ce6c896..448a4fff4 100644
--- a/crates/xtask/src/xtask.rs
+++ b/crates/xtask/src/xtask.rs
@@ -1,4 +1,4 @@
-//! See https://github.com/matklad/cargo-xtask
+//! See <https://github.com/matklad/cargo-xtask>
 //! This project now has a Justfile and a Makefile.
 //! Commands here are not always intended to be run directly
 //! by the user - add commands here which otherwise might
@@ -269,7 +269,7 @@ pub(crate) struct TmtProvisionArgs {
     /// Image name (e.g., "localhost/bootc")
     pub(crate) image: String,
 
-    /// VM name (defaults to "bootc-tmt-manual-<timestamp>")
+    /// VM name (defaults to "bootc-tmt-manual-`<timestamp>`")
     #[arg(value_name = "VM_NAME")]
     pub(crate) vm_name: Option<String>,
 }
