Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
620 changes: 489 additions & 131 deletions Cargo.lock

Large diffs are not rendered by default.

27 changes: 14 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
resolver = "2"
resolver = "3"
members = [
"crates/freighter",
"crates/freighter-api-types",
Expand All @@ -13,42 +13,43 @@ members = [
default-members = ["crates/freighter"]

[workspace.package]
version = "1.3.2"
version = "2.0.0"
authors = ["Noah Kennedy <nkennedy@cloudflare.com>", "Kornel Lesiński <kornel@cloudflare.com>"]
description = "Cloudflare's third-party Rust registry implementation"
edition = "2021"
edition = "2024"
license = "MIT OR Apache-2.0"
repository = "https://github.com/cloudflare/freighter"
publish = ["crates-io", "freighter", "freighter-staging", "freighter-local"]
keywords = ["registries", "freighter"]
categories = []

[workspace.dependencies]
freighter-api-types = { path = "crates/freighter-api-types", version = "1.0.0" }
freighter-client = { path = "crates/freighter-client", version = "1.0.0" }
freighter-auth = { path = "crates/freighter-auth", version = "1.0.0" }
freighter-pg-index = { path = "crates/freighter-pg-index", version = "1.0.0" }
freighter-fs-index = { path = "crates/freighter-fs-index", version = "1.0.0" }
freighter-server = { path = "crates/freighter-server", version = "1.0.0" }
freighter-storage = { path = "crates/freighter-storage", version = "1.0.0" }
freighter-api-types = { path = "crates/freighter-api-types", version = "2.0" }
freighter-client = { path = "crates/freighter-client", version = "2.0" }
freighter-auth = { path = "crates/freighter-auth", version = "2.0" }
freighter-pg-index = { path = "crates/freighter-pg-index", version = "2.0" }
freighter-fs-index = { path = "crates/freighter-fs-index", version = "2.0" }
freighter-server = { path = "crates/freighter-server", version = "2.0" }
freighter-storage = { path = "crates/freighter-storage", version = "2.0" }

anyhow = "1.0.93"
async-trait = "0.1.83"
aws-credential-types = "1.2.1"
aws-sdk-s3 = "1.61.0"
aws-sdk-s3 = "1.78.0"
axum = { version = "0.7.9", default-features = false }
axum-extra = { version = "0.9.6", features = ["json-lines"] }
base64 = "0.22"
bytes = "1.7.1"
chrono = { version = "0.4.38", default-features = false, features = ["std", "serde"] }
clap = { version = "4.5", default-features = false }
deadpool-postgres = { version = "0.14.0", features = ["serde"] }
futures-util = { version = "0.3.30", default-features = false }
futures-util = { version = "0.3.31", default-features = false, features = ["async-await-macro"] }
hyper = { version = "1.5.0", default-features = false }
hex = { version = "0.4.3", features = ["serde"] }
metrics = "0.24.0"
metrics-exporter-prometheus = { version = "0.16.0", default-features = false, features = ["http-listener"] }
postgres-types = "0.2.7"
rand = { version = "0.8.5", features = ["min_const_gen"] }
rand = { version = "0.9" }
reqwest = { version = "0.12.9", default-features = false, features = ["json"] }
semver = { version = "1.0.23", features = ["serde"] }
serde = { version = "1.0.215", features = ["derive"] }
Expand Down
3 changes: 2 additions & 1 deletion crates/freighter-api-types/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "freighter-api-types"
version = "1.0.0"
version.workspace = true
authors.workspace = true
categories.workspace = true
description.workspace = true
Expand Down Expand Up @@ -32,6 +32,7 @@ async-trait = { workspace = true, optional = true }
axum = { workspace = true, optional = true }
bytes = { workspace = true, optional = true }
chrono = { workspace = true }
hex = { workspace = true }
postgres-types = { workspace = true, optional = true, features = ["derive", "with-chrono-0_4"] }
serde = { workspace = true, optional = true }
serde_json = { workspace = true, optional = true }
Expand Down
19 changes: 16 additions & 3 deletions crates/freighter-api-types/src/index/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ pub struct SparseEntries {
pub last_modified: Option<DateTime<Utc>>,
}

#[derive(Debug, Clone, Copy)]
pub struct CrateVersionExists {
pub yanked: bool,
/// Sha256 of the package
pub tarball_checksum: [u8; 32],
}

/// A client for talking with a backing index database or storage medium.
///
/// Operations performed via this client MUST be atomic.
Expand All @@ -101,7 +108,9 @@ pub struct SparseEntries {
/// User actions should be authenticated before an operation is performed.
#[async_trait]
pub trait IndexProvider: Sync {
type Config;
type Config
where
Self: Sized;

async fn healthcheck(&self) -> anyhow::Result<()>;

Expand All @@ -115,7 +124,11 @@ pub trait IndexProvider: Sync {
/// will be returned.
async fn get_sparse_entry(&self, crate_name: &str) -> IndexResult<SparseEntries>;
/// Confirm that a particular crate and version pair exists, and return its yank status
async fn confirm_existence(&self, crate_name: &str, version: &Version) -> IndexResult<bool>;
async fn confirm_existence(
&self,
crate_name: &str,
version: &Version,
) -> IndexResult<CrateVersionExists>;
/// Yank a crate version.
async fn yank_crate(&self, crate_name: &str, version: &Version) -> IndexResult<()>;
/// Unyank a crate version
Expand All @@ -132,7 +145,7 @@ pub trait IndexProvider: Sync {
async fn publish(
&self,
version: &Publish,
checksum: &str,
tarball_checksum: [u8; 32],
end_step: Pin<&mut (dyn Future<Output = IndexResult<()>> + Send)>,
) -> IndexResult<CompletedPublication>;
/// List crates in the index, optionally specifying pagination.
Expand Down
7 changes: 4 additions & 3 deletions crates/freighter-api-types/src/index/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ pub struct CrateVersion {
/// Array of direct dependencies of the package.
pub deps: Vec<Dependency>,
/// A SHA256 checksum of the `.crate` file.
pub cksum: String,
#[serde(with = "hex")]
pub cksum: [u8; 32],
/// Set of features defined for the package.
///
/// Each feature maps to an array of features or dependencies it enables.
Expand All @@ -82,9 +83,9 @@ pub struct CrateVersion {
/// The current values are:
///
/// * 1: The schema as documented here, not including newer additions.
/// This is honored in Rust version 1.51 and newer.
/// This is honored in Rust version 1.51 and newer.
/// * 2: The addition of the `features2` field.
/// This is honored in Rust version 1.60 and newer.
/// This is honored in Rust version 1.60 and newer.
#[cfg_attr(any(feature = "index", feature = "client"), serde(default = "default_v"))]
pub v: u32,
/// This optional field contains features with new, extended syntax.
Expand Down
14 changes: 12 additions & 2 deletions crates/freighter-api-types/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ mod error;

#[async_trait]
pub trait StorageProvider {
async fn pull_crate(&self, name: &str, version: &str) -> StorageResult<FileResponse>;
async fn pull_crate(
&self,
name: &str,
version: &str,
tarball_checksum: [u8; 32],
) -> StorageResult<FileResponse>;
async fn put_crate(
&self,
name: &str,
Expand All @@ -19,7 +24,12 @@ pub trait StorageProvider {
sha256: [u8; 32],
) -> StorageResult<()>;
/// Called to undo a put after a failed index transaction
async fn delete_crate(&self, name: &str, version: &str) -> StorageResult<()>;
async fn delete_crate(
&self,
name: &str,
version: &str,
tarball_checksum: [u8; 32],
) -> StorageResult<()>;

async fn healthcheck(&self) -> anyhow::Result<()>;
}
Expand Down
2 changes: 1 addition & 1 deletion crates/freighter-auth/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "freighter-auth"
version = "1.0.0"
version.workspace = true
authors.workspace = true
categories.workspace = true
description.workspace = true
Expand Down
4 changes: 2 additions & 2 deletions crates/freighter-auth/src/base64_serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ pub fn deserialize<'de, D: Deserializer<'de>, const N: usize>(deserializer: D) -
}

fn visit_str<E: Error>(self, s: &str) -> Result<Self::Value, E> {
decode(s).ok_or(Error::invalid_value(Unexpected::Str(s), &self))
decode(s).ok_or_else(|| Error::invalid_value(Unexpected::Str(s), &self))
}

fn visit_borrowed_str<E: Error>(self, s: &'de str) -> Result<Self::Value, E> {
decode(s).ok_or(Error::invalid_value(Unexpected::Str(s), &self))
decode(s).ok_or_else(|| Error::invalid_value(Unexpected::Str(s), &self))
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/freighter-auth/src/cf_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ impl CfAccess {
AuthError::Unauthorized
})?
.claims;
drop(locked_keys);

let sub = claims.sub.filter(|s| !s.is_empty());
let sub_was_empty = sub.is_none();
Expand Down
8 changes: 3 additions & 5 deletions crates/freighter-auth/src/fs_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,10 @@ impl FsAuthProvider {
}

#[allow(clippy::unused_self)]
fn random_token(&self) -> AuthResult<BareToken> {
fn random_token(&self) -> BareToken {
use rand::Rng;
let mut token = [0; 21];
rand::thread_rng()
.try_fill(&mut token)
.map_err(|e| AuthError::ServiceError(e.into()))?;
rand::rng().fill(&mut token);
Ok(token)
}

Expand Down Expand Up @@ -152,7 +150,7 @@ impl AuthProvider for FsAuthProvider {

async fn register(&self, username: &str) -> AuthResult<String> {
let owners = &mut *self.owners_mut()?;
let bare_token = self.random_token()?;
let bare_token = self.random_token();
let hashed_token = self.hash_token(&bare_token);
let token_str = self.token_to_str(&bare_token);
owners.register(username, &hashed_token)?;
Expand Down
4 changes: 3 additions & 1 deletion crates/freighter-auth/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ use freighter_api_types::ownership::response::ListedOwner;

#[async_trait]
pub trait AuthProvider {
type Config;
type Config
where
Self: Sized;

async fn healthcheck(&self) -> anyhow::Result<()>;

Expand Down
4 changes: 2 additions & 2 deletions crates/freighter-auth/src/yes_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use crate::{AuthProvider, AuthResult};
use async_trait::async_trait;
use freighter_api_types::ownership::response::ListedOwner;
use rand::distributions::{Alphanumeric, DistString};
use rand::distr::{Alphanumeric, SampleString};

/// In the config specify `auth_allow_full_access_without_any_checks: true` to give full access to the registry,
/// including crate publishing, to anyone who can connect to it.
Expand Down Expand Up @@ -35,7 +35,7 @@ impl AuthProvider for YesAuthProvider {
}

async fn register(&self, _username: &str) -> AuthResult<String> {
let token = Alphanumeric.sample_string(&mut rand::thread_rng(), 32);
let token = Alphanumeric.sample_string(&mut rand::rng(), 32);

Ok(token)
}
Expand Down
2 changes: 1 addition & 1 deletion crates/freighter-client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "freighter-client"
version = "1.1.0"
version.workspace = true
description = "A very basic Cargo registry client, for testing"
authors.workspace = true
categories.workspace = true
Expand Down
32 changes: 22 additions & 10 deletions crates/freighter-fs-index/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use freighter_api_types::index::response::{
CompletedPublication, CrateVersion, Dependency, ListAll, ListAllCrateEntry,
ListAllCrateVersion, SearchResults, SearchResultsEntry, SearchResultsMeta,
};
use freighter_api_types::index::{IndexError, IndexProvider, IndexResult, SparseEntries};
use freighter_api_types::index::{
CrateVersionExists, IndexError, IndexProvider, IndexResult, SparseEntries,
};
use freighter_api_types::storage::MetadataStorageProvider;
use freighter_storage::fs::FsStorageProvider;
use freighter_storage::s3_client::S3StorageProvider;
Expand Down Expand Up @@ -121,7 +123,7 @@ impl FsIndexProvider {
path.push_str(&lc_crate_name[..1]);
}
_ => return None,
};
}
path.push('/');
path.push_str(lc_crate_name);
Some(path)
Expand Down Expand Up @@ -163,17 +165,27 @@ impl IndexProvider for FsIndexProvider {
.map(|(versions, _)| versions)
}

async fn confirm_existence(&self, crate_name: &str, version: &Version) -> IndexResult<bool> {
self.access_crate(crate_name)?
async fn confirm_existence(
&self,
crate_name: &str,
version: &Version,
) -> IndexResult<CrateVersionExists> {
let (versions, _) = self
.access_crate(crate_name)?
.shared()
.await
.deserialized()
.await
.map(|(versions, _)| versions.entries)?
.await?;

let e = versions
.entries
.iter()
.rfind(|e| &e.vers == version)
.map(|e| e.yanked)
.ok_or(IndexError::NotFound)
.ok_or(IndexError::NotFound)?;
Ok(CrateVersionExists {
yanked: e.yanked,
tarball_checksum: e.cksum,
})
}

async fn yank_crate(&self, crate_name: &str, version: &Version) -> IndexResult<()> {
Expand All @@ -187,7 +199,7 @@ impl IndexProvider for FsIndexProvider {
async fn publish(
&self,
publish: &Publish,
checksum: &str,
tarball_checksum: [u8; 32],
end_step: Pin<&mut (dyn Future<Output = IndexResult<()>> + Send)>,
) -> IndexResult<CompletedPublication> {
let release = CrateVersion {
Expand Down Expand Up @@ -215,7 +227,7 @@ impl IndexProvider for FsIndexProvider {
}
})
.collect(),
cksum: checksum.into(),
cksum: tarball_checksum,
features: publish.features.clone(),
yanked: false,
links: publish.links.clone(),
Expand Down
1 change: 1 addition & 0 deletions crates/freighter-pg-index/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ axum = { workspace = true }
chrono = { workspace = true }
deadpool-postgres = { workspace = true }
futures-util = { workspace = true }
hex = { workspace = true }
metrics = { workspace = true }
postgres-types = { workspace = true, features = ["derive", "with-chrono-0_4"] }
semver = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/freighter-pg-index/sql/confirm-existence.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
select cv.yanked
select cv.yanked, cv.cksum
from crates
join crate_versions cv on crates.id = cv.crate
where crates.name = $1
Expand Down
Loading
Loading