From a907941614166091a98158da9bb6be7d1b889632 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 23 Aug 2024 09:59:27 -0500 Subject: [PATCH 01/10] Add code of conduct and contributing guide --- CODE_OF_CONDUCT.md | 43 +++++++++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 21 +++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..5176811 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,43 @@ +# XMTP code of conduct + +This code of conduct applies within all XMTP community spaces, virtual and physical, and also applies when an individual is officially representing the XMTP community in public spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event. + +## Our pledge + +We as members pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. + +## Our conduct + +Examples of behaviors that contribute to a positive environment for our community include: + +- Demonstrating empathy and kindness toward other people +- Being collaborative and respectful of differing opinions, viewpoints, and experiences. A great protocol is built by many contributors learning and working together. +- Being bold yet intentional when presenting ideas. We are here to help ensure the world has access to secure and private communication, while also acknowledging that building successful protocols and ecosystems requires the application of logic and evidence. +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +- Focusing on what is best not just for us as individuals but for the overall community, and the people depending on XMTP for communication. + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of any kind +- Hate speech, trolling, shitposting, flaming, spamming, unsolicited advertising, baiting, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing othersโ€™ private information, such as a physical or email address, without their explicit permission + +## Moderation + +Community moderators are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. + +Community moderators have the right and responsibility to remove, edit, or reject comments, commits, code, issues, and other contributions that are not aligned to this code of conduct and will communicate reasons for moderation decisions when appropriate. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior can be reported to the community moderators responsible for enforcement at [conduct@xmtp.org](mailto:conduct@xmtp.org). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. All community moderators are obligated to respect the privacy and security of the reporter of any incident. + +Community moderators who do not follow or enforce the code of conduct in good faith may face temporary or permanent repercussions as determined by other community moderators. + +## Attribution + +The XMTP code of conduct is adapted from the Contributor Covenant, [version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html) and [version 1.4](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..bc0c50a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,21 @@ +# Contributing + +Thank you for considering contributing to this repo! Community contributions like yours are key to the development and adoption of XMTP. Your questions, feedback, suggestions, and code contributions are welcome! + +## โ” Questions + +Have a question about how to build with XMTP? Ask your question and learn with the community in the [XMTP Community Forums](https://community.xmtp.org/). + +## ๐Ÿž Bugs + +Report a bug using [GitHub Issues](https://github.com/xmtp/xmtp-message-history-server/issues). + +## โœจ Feature requests + +Request a feature using [GitHub Issues](https://github.com/xmtp/xmtp-message-history-server/issues). + +## ๐Ÿ”€ Pull requests + +PRs are encouraged, but consider starting with a feature request to temperature-check first. If the PR involves a major change to the protocol, the work should be fleshed out as an [XMTP Improvement Proposal](https://community.xmtp.org/t/xip-0-xip-purpose-process-guidelines/475) before work begins. + +After a pull request is submitted, a single approval is required to merge it. From b4b84858dde4248d7cc0b38ff8cffa444a998854 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 23 Aug 2024 09:59:50 -0500 Subject: [PATCH 02/10] Update GitHub workflows --- .../workflows/build_and_push_docker_image.yml | 11 ++++---- .github/workflows/build_and_test.yml | 25 +++++++++++-------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build_and_push_docker_image.yml b/.github/workflows/build_and_push_docker_image.yml index 392ac77..cb3f13e 100644 --- a/.github/workflows/build_and_push_docker_image.yml +++ b/.github/workflows/build_and_push_docker_image.yml @@ -14,11 +14,11 @@ jobs: contents: read packages: write steps: - - name: Check out the repo - uses: actions/checkout@v2 + - name: Checkout + uses: actions/checkout@v4 - name: Log in to the Container registry - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -26,12 +26,12 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v5 with: images: ghcr.io/xmtp/message-history-server - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 id: push with: context: . @@ -49,4 +49,3 @@ jobs: variable-name: message_history_server_docker_image variable-value: "ghcr.io/xmtp/message-history-server@${{ steps.push.outputs.digest }}" variable-value-required-prefix: "ghcr.io/xmtp/message-history-server@sha256:" - diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 31000a2..cf94f17 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -1,22 +1,25 @@ -name: Rust +name: XMTP Message History Server on: push: - branches: [ "main" ] + branches: + - main pull_request: - branches: [ "main" ] + branches: + - main env: CARGO_TERM_COLOR: always jobs: - build: + build-test: + runs-on: warp-ubuntu-latest-x64-16x + steps: + - name: Checkout + uses: actions/checkout@v4 - runs-on: ubuntu-latest + - name: Build + run: cargo build --verbose - steps: - - uses: actions/checkout@v3 - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --verbose + - name: Test + run: cargo test --verbose From 39423ef82a61bc19e9dc2889d9875468651b09a5 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 23 Aug 2024 10:00:44 -0500 Subject: [PATCH 03/10] Remove header requirements --- src/main.rs | 139 +++------------------------------------------------- 1 file changed, 6 insertions(+), 133 deletions(-) diff --git a/src/main.rs b/src/main.rs index 561a53b..63f4a3d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,13 +5,8 @@ use std::path::PathBuf; use actix_web::http::StatusCode; use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer, Responder}; use futures::StreamExt; -use hex::{decode, encode}; -use hmac::{Hmac, Mac}; -use sha2::Sha256; use uuid::Uuid; -type HmacSha256 = Hmac; - async fn upload_file(_req: HttpRequest, mut payload: web::Payload) -> impl Responder { // Create a new UUID for the file. let file_id = Uuid::new_v4(); @@ -49,21 +44,8 @@ async fn upload_file(_req: HttpRequest, mut payload: web::Payload) -> impl Respo HttpResponse::Ok().body(file_id.to_string()) } -async fn get_file(req: HttpRequest, path: web::Path) -> impl Responder { - // Extract the X-HMAC header - let hmac_header = match req.headers().get("X-HMAC") { - Some(value) => value.to_str().unwrap_or_default(), - None => return HttpResponse::BadRequest().body("Missing X-HMAC header"), - }; - - // Extract the X-SIGNING-KEY header - let signing_key = match req.headers().get("X-SIGNING-KEY") { - Some(value) => value.to_str().unwrap_or_default(), - None => return HttpResponse::BadRequest().body("Missing X-SIGNING-KEY header"), - }; - +async fn get_file(_req: HttpRequest, path: web::Path) -> impl Responder { let file_id = path.to_string(); - let file_path: PathBuf = format!("uploads/{}", file_id).into(); if file_path.exists() { @@ -77,52 +59,14 @@ async fn get_file(req: HttpRequest, path: web::Path) -> impl Responder { return HttpResponse::InternalServerError().body("Failed to read file"); } - // Compute the HMAC for the file content - let mut mac = HmacSha256::new_from_slice(signing_key.as_bytes()) - .expect("HMAC can take key of any size"); - mac.update(&buffer); - let result_hmac = encode(mac.finalize().into_bytes()); - - // Verify if the provided HMAC matches the computed one - if hmac_header == result_hmac { - HttpResponse::Ok() // Serve the file content - .content_type("application/octet-stream") - .body(buffer) - } else { - HttpResponse::Unauthorized().body("Invalid HMAC") - } + HttpResponse::Ok() // Serve the file content + .content_type("application/octet-stream") + .body(buffer) } else { HttpResponse::NotFound().finish() } } -/// Verifies the HMAC of the request. -#[allow(dead_code)] -fn verify_hmac( - hmac_header: &str, - file_bytes: &[u8], - signing_key: &[u8], -) -> Result<(), HttpResponse> { - // Decode the hex HMAC - let received_hmac = match decode(hmac_header) { - Ok(hmac) => hmac, - Err(_) => return Err(HttpResponse::BadRequest().body("Invalid HMAC format")), - }; - - // Create an instance of the HMAC-SHA256 - let mut mac = HmacSha256::new_from_slice(signing_key).expect("Insufficient HMAC key size"); - - // Input the data to the HMAC instance - mac.update(file_bytes); - - // Compute the HMAC and compare it with the received HMAC - match mac.verify_slice(&received_hmac) { - Ok(_) => Ok(()), - Err(_) => Err(HttpResponse::Unauthorized().body("HMAC verification failed")), - } -} - - async fn health_check() -> impl Responder { HttpResponse::Ok().finish() } @@ -151,51 +95,12 @@ mod tests { use actix_web::test; use std::path::Path; - const SIGNING_KEY: &[u8] = b"TEST_SECRET_KEY"; - - // Test helper function to create a HMAC signature - fn create_hmac_signature(signing_key: &[u8], data: &[u8]) -> String { - let mut mac = - Hmac::::new_from_slice(signing_key).expect("HMAC can take key of any size"); - mac.update(data); - encode(mac.finalize().into_bytes()) - } - - // Tests the HMAC verification logic - #[test] - async fn test_hmac_verification() { - // Test valid HMAC passes as Ok(()) - let correct_payload = b"correct payload"; - let correct_hmac = create_hmac_signature(SIGNING_KEY, correct_payload); - let verify_correct = verify_hmac(&correct_hmac, correct_payload, SIGNING_KEY); - - assert!(verify_correct.is_ok(), "Should succeed with correct HMAC"); - - // Test invvalid HMAC returns an HttpResponse()) - let incorrect_payload = b"incorrect payload"; - let verify_incorrect = verify_hmac(&correct_hmac, incorrect_payload, SIGNING_KEY); - assert!(verify_incorrect.is_err()); - } - #[actix_web::test] async fn test_upload_file() { let app = test::init_service(App::new().route("/upload", web::post().to(upload_file))).await; - // Test with incorrect HMAC - let req = test::TestRequest::get() - .uri("/upload") - .insert_header(("X-HMAC", "incorrect_hmac")) - .to_request(); - let resp = test::call_service(&app, req).await; - assert!( - resp.status().is_client_error(), - "Should fail with incorrect HMAC" - ); - let correct_payload = b"correct payload"; - let correct_hmac = create_hmac_signature(SIGNING_KEY, correct_payload); - assert!(!correct_hmac.is_empty()); let req = test::TestRequest::post() .uri("/upload") @@ -212,11 +117,7 @@ mod tests { #[actix_web::test] async fn test_get_file() { - let signing_key = std::str::from_utf8(SIGNING_KEY).unwrap().to_string(); - let file_contents = b"this too shall pass!"; // Simulated file contents - let correct_hmac = create_hmac_signature(SIGNING_KEY, file_contents); - let incorrect_hmac = String::from("none shall pass"); let uploads_path = "uploads"; std::fs::create_dir_all(uploads_path).unwrap(); @@ -231,41 +132,13 @@ mod tests { ) .await; - // Test with correct HMAC let req = test::TestRequest::get() .uri(&format!("/files/{file_id}")) - .insert_header(("X-HMAC", correct_hmac.clone())) - .insert_header(("X-SIGNING-KEY", signing_key)) .to_request(); let resp = test::call_service(&app, req).await; - assert!( - resp.status().is_success(), - "Should succeed with correct HMAC" - ); + assert!(resp.status().is_success(), "Should succeed"); - // Test with incorrect HMAC - let req = test::TestRequest::get() - .uri(&format!("/files/{file_id}")) - .insert_header(("X-HMAC", incorrect_hmac)) - .insert_header(("X-SIGNING-KEY", "doesnt matter")) - .to_request(); - let resp = test::call_service(&app, req).await; - assert!( - resp.status().is_client_error(), - "Should fail with incorrect HMAC" - ); - - // Test with incorrect Signing Key - let req = test::TestRequest::get() - .uri(&format!("/files/{file_id}")) - .insert_header(("X-HMAC", correct_hmac)) - .insert_header(("X-SIGNING-KEY", "wrong")) - .to_request(); - let resp = test::call_service(&app, req).await; - assert!( - resp.status().is_client_error(), - "Should fail with incorrect Signing Key" - ); + std::fs::remove_dir_all(uploads_path).expect("Failed to remove uploads directory"); } } From ab7f8fa47c6ef47baa5b3f97e7b8bb079e8ab5b6 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 23 Aug 2024 10:00:57 -0500 Subject: [PATCH 04/10] Update dependencies --- Cargo.lock | 175 ++++++++++++++++++++++++++--------------------------- Cargo.toml | 14 ++--- 2 files changed, 90 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index de4eab8..071bf50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,9 +21,9 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.6.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d223b13fd481fc0d1f83bb12659ae774d9e3601814c68a0bc539731698cca743" +checksum = "d48f96fc3003717aeb9856ca3d02a8c7de502667ad76eeacd830b48d2e91fac4" dependencies = [ "actix-codec", "actix-rt", @@ -70,24 +70,25 @@ dependencies = [ [[package]] name = "actix-router" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22475596539443685426b6bdadb926ad0ecaefdfc5fb05e5e3441f15463c511" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" dependencies = [ "bytestring", + "cfg-if", "http", "regex", + "regex-lite", "serde", "tracing", ] [[package]] name = "actix-rt" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" dependencies = [ - "actix-macros", "futures-core", "tokio", ] @@ -132,9 +133,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.5.1" +version = "4.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a6556ddebb638c2358714d853257ed226ece6023ef9364f23f0c70737ea984" +checksum = "9180d76e5cc7ccbc4d60a506f2c727730b154010262df5b910eb17dbe4b8cb38" dependencies = [ "actix-codec", "actix-http", @@ -154,6 +155,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", + "impl-more", "itoa", "language-tags", "log", @@ -161,6 +163,7 @@ dependencies = [ "once_cell", "pin-project-lite", "regex", + "regex-lite", "serde", "serde_json", "serde_urlencoded", @@ -172,9 +175,9 @@ dependencies = [ [[package]] name = "actix-web-codegen" -version = "4.2.2" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1f50ebbb30eca122b188319a4398b3f7bb4a8cdf50ecfb73bfc6a3c3ce54f5" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" dependencies = [ "actix-router", "proc-macro2", @@ -257,9 +260,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" @@ -284,9 +287,9 @@ dependencies = [ [[package]] name = "brotli" -version = "3.5.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -295,9 +298,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.5.1" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -409,7 +412,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", - "subtle", ] [[package]] @@ -586,9 +588,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "h2" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -609,21 +611,6 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - [[package]] name = "http" version = "0.2.12" @@ -657,6 +644,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "impl-more" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d" + [[package]] name = "indexmap" version = "2.2.6" @@ -932,6 +925,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + [[package]] name = "regex-syntax" version = "0.8.3" @@ -1038,17 +1037,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -1083,12 +1071,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - [[package]] name = "syn" version = "1.0.109" @@ -1113,21 +1095,22 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -1146,9 +1129,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -1260,9 +1243,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", "serde", @@ -1295,7 +1278,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -1315,17 +1307,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -1336,9 +1329,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -1348,9 +1341,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -1360,9 +1353,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -1372,9 +1371,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -1384,9 +1383,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -1396,9 +1395,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -1408,20 +1407,16 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "xmtp-message-history-server" -version = "0.1.0" +version = "0.2.0" dependencies = [ - "actix-rt", "actix-web", "futures", - "hex", - "hmac", - "sha2", "tempfile", "uuid", ] diff --git a/Cargo.toml b/Cargo.toml index 20b0cda..25d23d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,13 @@ [package] name = "xmtp-message-history-server" -version = "0.1.0" +version = "0.2.0" edition = "2021" [dependencies] -actix-web = "4.0" +actix-web = "4.9.0" futures = "0.3.30" -hex = "0.4.3" -hmac = "0.12.1" -sha2 = "0.10.8" -uuid = { version = "1", features = ["v4", "serde"] } +uuid = { version = "1.10.0", features = ["v4", "serde"] } [dev-dependencies] -actix-rt = "2.9.0" -actix-web = "4.0" -tempfile = "3.10.1" +actix-web = "4.9.0" +tempfile = "3.12.0" From c643fa5e34d043b4769a4eee3d93c2485d85df3b Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 23 Aug 2024 10:01:38 -0500 Subject: [PATCH 05/10] Update .gitignore --- .gitignore | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 6cead8c..8662fba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,2 @@ -/target -uploads/ -examples/myenv -examples/messages* -examples/*.aes +target +uploads From 2a6ba2aed81c5fabc228af24872366d478d18b70 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 23 Aug 2024 10:31:05 -0500 Subject: [PATCH 06/10] Refactor example to use Node --- examples/.node-version | 1 + examples/.nvmrc | 1 + examples/download.js | 21 +++++++++++ examples/fetch-bundle.py | 71 -------------------------------------- examples/package.json | 6 ++++ examples/requirements.txt | 2 -- examples/test_bundle.jsonl | 5 --- examples/test_file.txt | 1 + examples/upload-bundle.py | 56 ------------------------------ examples/upload.js | 26 ++++++++++++++ 10 files changed, 56 insertions(+), 134 deletions(-) create mode 100644 examples/.node-version create mode 100644 examples/.nvmrc create mode 100644 examples/download.js delete mode 100644 examples/fetch-bundle.py create mode 100644 examples/package.json delete mode 100644 examples/requirements.txt delete mode 100644 examples/test_bundle.jsonl create mode 100644 examples/test_file.txt delete mode 100644 examples/upload-bundle.py create mode 100644 examples/upload.js diff --git a/examples/.node-version b/examples/.node-version new file mode 100644 index 0000000..9075659 --- /dev/null +++ b/examples/.node-version @@ -0,0 +1 @@ +20.15.0 diff --git a/examples/.nvmrc b/examples/.nvmrc new file mode 100644 index 0000000..9075659 --- /dev/null +++ b/examples/.nvmrc @@ -0,0 +1 @@ +20.15.0 diff --git a/examples/download.js b/examples/download.js new file mode 100644 index 0000000..e6574e7 --- /dev/null +++ b/examples/download.js @@ -0,0 +1,21 @@ +const BUNDLE_ID = process.env.BUNDLE_ID; + +if (!BUNDLE_ID) { + console.error("BUNDLE_ID must be present in the environment"); + process.exit(1); +} + +console.log(`Downloading bundle: ${BUNDLE_ID}`); + +const url = `http://0.0.0.0:5558/files/${BUNDLE_ID}`; + +const response = await fetch(url); + +if (!response.ok) { + console.error(`Failed to download bundle. Status code: ${response.status}`); + process.exit(1); +} + +const text = await response.text(); + +console.log(`Downloaded bundle with content: ${text}`); diff --git a/examples/fetch-bundle.py b/examples/fetch-bundle.py deleted file mode 100644 index 2cfd4c4..0000000 --- a/examples/fetch-bundle.py +++ /dev/null @@ -1,71 +0,0 @@ -import pyAesCrypt -import requests -import os - -def decrypt(source, key): - parts = source.rsplit(".aes", 1) - output = parts[0] if len(parts) > 1 else source - key_str = key.decode('utf-8') - pyAesCrypt.decryptFile(source, output, key_str) - return - -def download_messages_bundle(bundle_id, hmac_value, signing_key, aes_key): - """ - Downloads a file from the specified endpoint using a GET request with X-HMAC & X-SIGNING-KEY headers. - - Parameters: - - bundle_id (str): The unique identifier for the file. - - hmac_value (str): The HMAC signature value of the file with `bundle_id`. - - signing_key (str): The key used to sign the bundle. - - aes_key (str): The AES key used to decrypt the downloaded file. - """ - - # Construct the URL with the file's BUNDLE_ID - bundle_id_str = bundle_id.decode('utf-8') - url = f"http://0.0.0.0:5558/files/{bundle_id_str}" - - # Send the GET request with the X-HMAC header - hmac_value_str = hmac_value.decode('utf-8') - headers = {'X-HMAC': hmac_value_str, 'X-SIGNING-KEY': signing_key} - response = requests.get(url, headers=headers) - - if response.status_code == 200: - print("File downloaded successfully.") - # Here, you will want to save the file content to a file. - file_name = f"messages_bundle_{bundle_id_str}.aes" - with open(file_name, 'wb') as file: - file.write(response.content) - - decrypt(file_name, aes_key) - print(f"Successfully decrypted {file_name}") - - else: - print(f"Failed to download file. Status code: {response.status_code} Response: {response.text}") - - -if __name__ == "__main__": - # The assigned bundle_id returned from calling `python upload-bundle.py` - bundle_id = os.environ.get("BUNDLE_ID", "").encode() - if not bundle_id: - print("BUNDLE_ID environment variable is not set.") - exit(1) - - # The value from calculating the hmac signature for the uploaded messages bundle file - hmac_value = os.environ.get("HMAC_VALUE", "").encode() - if not hmac_value: - print("HMAC_VALUE environment variable is not set.") - exit(1) - - # Ensure the signing key is not empty - signing_key = os.environ.get("SIGNING_KEY", "").encode() - if not signing_key: - print("SIGNING_KEY environment variable is not set.") - exit(1) - - # Ensure the aes key is not empty - aes_key = os.environ.get("AES_KEY", "").encode() - if not aes_key: - print("AES_KEY environment variable is not set.") - exit(1) - - download_messages_bundle(bundle_id, hmac_value, signing_key, aes_key) diff --git a/examples/package.json b/examples/package.json new file mode 100644 index 0000000..1bb55ed --- /dev/null +++ b/examples/package.json @@ -0,0 +1,6 @@ +{ + "name": "xmtp-message-history-server-example", + "version": "0.0.0", + "private": true, + "type": "module" +} diff --git a/examples/requirements.txt b/examples/requirements.txt deleted file mode 100644 index 7eb26c4..0000000 --- a/examples/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -requests==2.31.0 -pyAesCrypt===6.1.1 diff --git a/examples/test_bundle.jsonl b/examples/test_bundle.jsonl deleted file mode 100644 index 464bfd6..0000000 --- a/examples/test_bundle.jsonl +++ /dev/null @@ -1,5 +0,0 @@ -{"id": "88facc023d3e4a1db3f9d8e344fb9c5729e34e8df58dfc1bb3e7fcd8efc8e5f1", "group_id": "4a3b711b8fe14d428f8e8b8b82b8e8b82b8e8b82b8e8b82b8e8b82b8e8b82b8e", "decrypted_message_bytes": "48656c6c6f2c20776f726c6421", "sent_at_ns": 1625097600000000000, "kind": 1, "sender_installation_id": "a1b2c3d4e5f678901234567890abcdef", "sender_account_address": "0xAbCdEf1234567890aBcDeF1234567890aBCdEf12", "delivery_status": 1} -{"id": "7fbacc023d3e4a1db3f9d8e344fb9c5729e34e8df58dfc1bb3e7fcd8efc8e5e2", "group_id": "5b3b711b8fe14d428f8e8b8b82b8e8b82b8e8b82b8e8b82b8e8b82b8e8b82b8f", "decrypted_message_bytes": "5768617427732075703f", "sent_at_ns": 1625198600000000000, "kind": 2, "sender_installation_id": "b2c3d4e5f678901234567890abcdefa1", "sender_account_address": "0xBCdEf1234567890aBcDeF1234567890aBCdEf34", "delivery_status": 2} -{"id": "6cbacc023d3e4a1db3f9d8e344fb9c5729e34e8df58dfc1bb3e7fcd8efc8e5d3", "group_id": "6c3b711b8fe14d428f8e8b8b82b8e8b82b8e8b82b8e8b82b8e8b82b8e8b82b8d", "decrypted_message_bytes": "53656520796f7520736f6f6e21", "sent_at_ns": 1625299600000000000, "kind": 1, "sender_installation_id": "c3d4e5f678901234567890abcdefa1b2", "sender_account_address": "0x1234567890aBcDeF1234567890aBCdEf12345678", "delivery_status": 1} -{"id": "5dbacc023d3e4a1db3f9d8e344fb9c5729e34e8df58dfc1bb3e7fcd8efc8e5c4", "group_id": "7d3b711b8fe14d428f8e8b8b82b8e8b82b8e8b82b8e8b82b8e8b82b8e8b82b7c", "decrypted_message_bytes": "4772656174206a6f6221", "sent_at_ns": 1625390600000000000, "kind": 2, "sender_installation_id": "d4e5f678901234567890abcdefa1b2c3", "sender_account_address": "0xf1234567890aBCdEf1234567890aBcDeF1234567", "delivery_status": 2} -{"id": "4ebacc023d3e4a1db3f9d8e344fb9c5729e34e8df58dfc1bb3e7fcd8efc8e5b5", "group_id": "8e3b711b8fe14d428f8e8b8b82b8e8b82b8e8b82b8e8b82b8e8b82b8e8b82b8e", "decrypted_message_bytes": "4c65742773206d6565742075702e", "sent_at_ns": 1625491600000000000, "kind": 1, "sender_installation_id": "e5f678901234567890abcdefa1b2c3d4", "sender_account_address": "0x4567890aBCdEf1234567890aBcDeF1234567890a", "delivery_status": 1} diff --git a/examples/test_file.txt b/examples/test_file.txt new file mode 100644 index 0000000..5dd01c1 --- /dev/null +++ b/examples/test_file.txt @@ -0,0 +1 @@ +Hello, world! \ No newline at end of file diff --git a/examples/upload-bundle.py b/examples/upload-bundle.py deleted file mode 100644 index 0f4f49b..0000000 --- a/examples/upload-bundle.py +++ /dev/null @@ -1,56 +0,0 @@ -import hmac -import hashlib -import pyAesCrypt -import requests -import os - -file_path = "test_bundle.jsonl" - -def encrypt(source, key): - key_str = key.decode('utf-8') - output = source + ".aes" - pyAesCrypt.encryptFile(source, output, key_str) - return output - -def upload_message_bundle(file_path, signing_key): - """ - Uploads a file to the specified endpoint. The file should be encrypted. - - Parameters: - - file_path (str): The path to the file to upload. - - signing_key (str): The key used to sign the HMAC. - """ - # The request payload consisting of a message history bundle - with open(file_path, 'rb') as file: - file_content = file.read() - - # Compute the HMAC - hmac_instance = hmac.new(signing_key, file_content, hashlib.sha256) - hmac_hex = hmac_instance.hexdigest() - print(f"HMAC: {hmac_hex}") - - # Send the request with the HMAC header - headers = {'X-HMAC': hmac_hex} - response = requests.post('http://0.0.0.0:5558/upload', headers=headers, data=file_content) - # Log the response - print(f"Response Status Code: {response.status_code}") - print(f"Response Body: {response.text}") - -if __name__ == "__main__": - # Ensure the signing key is not empty - signing_key = os.environ.get("SIGNING_KEY", "").encode() - if not signing_key: - print("SIGNING_KEY environment variable is not set.") - exit(1) - - # Ensure the aes key is not empty - aes_key = os.environ.get("AES_KEY", "").encode() - if not aes_key: - print("AES_KEY environment variable is not set.") - exit(1) - - encrypted_file = encrypt(file_path, aes_key) - - upload_message_bundle(encrypted_file, signing_key) - - diff --git a/examples/upload.js b/examples/upload.js new file mode 100644 index 0000000..fe7caeb --- /dev/null +++ b/examples/upload.js @@ -0,0 +1,26 @@ +import { readFileSync } from "node:fs"; +import { join } from "node:path"; + +const file = readFileSync(join(import.meta.dirname, "test_file.txt"), { + encoding: "utf-8", +}); + +console.log(`Uploading file test_file.txt with content: ${file}`); + +const response = await fetch("http://0.0.0.0:5558/upload", { + method: "POST", + body: file, +}); + +if (response.ok) { + console.log("File uploaded successfully"); +} else { + console.error(`Failed to upload file. Status code: ${response.status}`); + process.exit(1); +} + +const bundleId = await response.text(); +console.log(`Bundle ID: ${bundleId}`); +console.log( + `Run "BUNDLE_ID=${bundleId} node download.js" to download the file` +); From fc7597d9ce407436d2da02ca35b9e50193bc7c6d Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 23 Aug 2024 10:48:30 -0500 Subject: [PATCH 07/10] Update README --- README.md | 115 ++++++++++++++++++++++++++---------------------------- 1 file changed, 55 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 526a860..07e4357 100644 --- a/README.md +++ b/README.md @@ -1,108 +1,103 @@ # XMTP Message History Server -A simple, asynchronous file server provided as an example to support XMTP Message History transfers. +A simple, asynchronous file server provided as an example to support XMTP Message History transfers. -Running this server allows XMTP service providers to provide the ability for users to securely upload message history bundles via a `POST` request and retrieve them via a `GET` request from another authorized app or device, using a unique ID assigned upon upload. -It is expected that these uploaded bundles should be: encrypted, short-lived, non-guessable, with scoped access to only authorized parties. +Running this server allows XMTP service providers to provide the ability for users to securely upload message history bundles via a `POST` request and retrieve them via a `GET` request from another authorized app or device, using a unique ID assigned upon upload. + +It is expected that these uploaded bundles should be: encrypted, short-lived, and non-guessable. ![Message History Diagram](./MessageHistory.svg) -## Getting Started +## Getting started ### Prerequisites -Ensure you have the following installed: +#### Rust + +Rust and Cargo can be installed from [https://rustup.rs](https://rustup.rs). + +#### Node + +Node is required to run the example client scripts. -- Rust and Cargo. You can install them both from [https://rustup.rs](https://rustup.rs) -- To run the scripts in the [`examples/`](./examples/) folder, ensure python is correctly installed on your system. +Please make sure you have a compatible version as specified in `package.json`. We recommend using a Node version manager such as [nvm](https://github.com/nvm-sh/nvm) or [nodenv](https://github.com/nodenv/nodenv). -### Installing +### Install Clone the repository to your local machine: - git clone https://github.com/xmtp/xmtp-message-history-server.git - cd xmtp-message-history-server +```bash +git clone https://github.com/xmtp/xmtp-message-history-server.git +cd xmtp-message-history-server +``` Build the project: - cargo build +```bash +cargo build +``` Run the server: - cargo run +```bash +cargo run +``` The server will start running on http://0.0.0.0:5558. ## Usage -Set the `AES_KEY` and `SIGNING_KEY` environment variables. +### Upload a file - export AES_KEY=only-authorized-installations-should-share-this-key - export SIGNING_KEY=used-to-sign-the-message-bundle - -### Uploading a File - -To upload a file, send a POST request to http://0.0.0.0:5558/upload with the file data in the request body. +To upload a file, send a POST request to http://0.0.0.0:5558/upload with the file data in the request body. The server will return a unique ID for the uploaded file. -Example using curl: - - curl -X POST http://0.0.0.0:5558/upload - -F "file=@path/to/your/message_bundle.aes" +Example using cURL: -### Retrieving a File - -To retrieve an uploaded file, you must possess the `HMAC_SIGNATURE` and `SIGNING_KEY` of the message_bundle. - -The request must include - -- an `X-HMAC` header using `SHA256` as the hashing algorithm. -- an `X-SIGNING-KEY` header set to the `SIGNING_KEY` +```bash +curl -X POST http://0.0.0.0:5558/upload --data "@./examples/test_file.txt" +``` +### Retrieve a file Send a GET request to http://0.0.0.0:5558/files/{id}, where {id} is the unique ID returned by the server during the upload. -Example using curl: - - curl http://0.0.0.0:5558/files/{id} - -H "X-HMAC: " - -H "X-SIGNING-KEY: " - --output retrieved_file.aes +Example using cURL: -### Decrypting a File +```bash +curl http://0.0.0.0:5558/files/{id} --output test_file.txt +``` -To decrypt the downloaded file, you must possess the `AES_KEY` that was used to encrypt the message_bundle. +### Example -### Example Reference Client +An example set of Node scripts are available in the `examples/` folder demonstrating the upload and download of a file. -An example set of python scripts are available in the `examples/` folder demonstrating the full roundtrip of: -- encrypting, signing, uploading in `upload-bundle.py` -- authenticating (using the hmac key), downloading, and decrypting in `fetch-bundle.py` +#### Upload file -Set up a virtual environment +```bash +node upload.js +``` - python3 -m venv myenv - source myenv/bin/activate +This script will output the bundle ID, which can be used to download the file. -Install the dependencies +Example output: - pip3 install -r requirements.txt +``` +Uploading file test_file.txt with content: Hello, world! +File uploaded successfully +Bundle ID: aabbc983-0f0e-4fc8-911f-d4e669e48237 +Run "BUNDLE_ID=aabbc983-0f0e-4fc8-911f-d4e669e48237 node download.js" to download the file +``` -Run the uploader script +#### Download file - python3 upload-bundle.py +Using the bundle ID from the previous step: -Fetch the uploaded file - - # export BUNDLE_ID= - # export HMAC_VALUE= - python3 fetch-bundle.py +```bash +BUNDLE_ID=aabbc983-0f0e-4fc8-911f-d4e669e48237 node download.js +``` ## Contributing -Contributions are welcome! Please feel free to submit a pull request or open an issue. - -## License - -This project is licensed under the MIT License - see the LICENSE file for details. +See our [contribution guide](./CONTRIBUTING.md) to learn more about contributing to this project. From d455f563e2140321bcb3bc79ce5cf2ed9f83c81c Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 23 Aug 2024 10:52:04 -0500 Subject: [PATCH 08/10] Change runner --- .github/workflows/build_and_test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index cf94f17..0e26d77 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -13,7 +13,8 @@ env: jobs: build-test: - runs-on: warp-ubuntu-latest-x64-16x + # runs-on: warp-ubuntu-latest-x64-16x + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 From f10fff0651e937c28f1c4dcce93512baeb43cfd4 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 23 Aug 2024 10:56:09 -0500 Subject: [PATCH 09/10] Update workflow --- .github/workflows/build_and_test.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 0e26d77..1b7ca6f 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -12,15 +12,30 @@ env: CARGO_TERM_COLOR: always jobs: - build-test: + build-lint-test: # runs-on: warp-ubuntu-latest-x64-16x runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 + - name: Update rust toolchains + run: rustup update + + - name: Cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + . + - name: Build - run: cargo build --verbose + run: cargo build + + - name: Run clippy and fail on warnings + run: cargo clippy --all-features --all-targets --no-deps -- -Dwarnings + + - name: Run format check + run: cargo fmt --check - name: Test - run: cargo test --verbose + run: cargo test From 018c550c28d69d3624acd7c10753d229e2e04a53 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 23 Aug 2024 11:02:01 -0500 Subject: [PATCH 10/10] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 07e4357..28edf3e 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Rust and Cargo can be installed from [https://rustup.rs](https://rustup.rs). Node is required to run the example client scripts. -Please make sure you have a compatible version as specified in `package.json`. We recommend using a Node version manager such as [nvm](https://github.com/nvm-sh/nvm) or [nodenv](https://github.com/nodenv/nodenv). +Please make sure you have a compatible version as specified in `examples/.node-version`. We recommend using a Node version manager such as [nvm](https://github.com/nvm-sh/nvm) or [nodenv](https://github.com/nodenv/nodenv). ### Install