Skip to content

feat: deliver production-ready Rust SDK with public testnet sn-api E2E CI#1

Open
mateeullahmalik wants to merge 5 commits intomainfrom
feat/readme-config-tests-hardening
Open

feat: deliver production-ready Rust SDK with public testnet sn-api E2E CI#1
mateeullahmalik wants to merge 5 commits intomainfrom
feat/readme-config-tests-hardening

Conversation

@mateeullahmalik
Copy link
Contributor

@mateeullahmalik mateeullahmalik commented Mar 16, 2026

What users can do with this SDK

lumera-sdk-rs now enables Rust apps/services to run the full Cascade workflow against Lumera:

  1. Configure once, run anywhere

    • Load settings from defaults, env, .env, TOML, or JSON (SdkSettings)
    • Override per deployment via env vars
  2. Query chain state needed for uploads

    • Fetch action module params
    • Fetch action fee amount for file size
  3. Register Cascade actions on-chain

    • Build metadata + signatures
    • Broadcast MsgRequestAction
    • Retry safely on sequence mismatch
    • Extract action_id from tx result
  4. Upload via public/private sn-api

    • Start cascade upload task
    • Poll status until terminal state
  5. Download + verify integrity

    • Request download task
    • Poll status and fetch output bytes
    • Verify end-to-end hash match
  6. Run end-to-end examples immediately

    • examples/custom_config.rs
    • examples/from_env_settings.rs
    • examples/golden_devnet.rs (register -> upload -> download -> hash)

Facilities / features added in this PR

SDK surface

  • CascadeSdk high-level orchestration layer
  • ChainClient for action params/fees + tx registration path
  • SnApiClient for upload/download task operations
  • Deterministic helpers for data hash/layout/index/ID derivation
  • Config bridge SdkSettings -> CascadeConfig

Configuration & integration

  • Multi-source config loading (env, .env, toml, json)
  • Strong defaults for local/devnet
  • Env override model for deployment-time control

Developer experience

  • Makefile workflow: build, test, fmt-check, lint, doc, check, golden, e2e-pr
  • Expanded README with usage, CI, and runbook-level guidance
  • Example index in examples/README.md

CI / quality gates

  • Lint/format/test/docs/security workflows
  • PR E2E workflow targeting public testnet/public sn-api
  • Public E2E script with endpoint sanity checks + retries
  • Optional full golden flow in CI when testnet secrets are set

Robustness fixes included

  • Env isolation in config tests (prevents flaky overrides)
  • Sequence retry path refreshes account state under contention
  • sn-api readiness and public endpoint retries in E2E scripts
  • Consistent default SNAPI_BASE port across code/docs/examples

Validation status

  • Local cargo test --workspace --all-features --all-targets
  • Local ./.github/scripts/e2e_public_pr.sh smoke mode ✅
  • Full register/upload/download/hash path is wired for CI when secrets are present ✅

@roomote-v0
Copy link

roomote-v0 bot commented Mar 16, 2026

Rooviewer Clock   See task

Reviewed 1e5c02e (clippy dead-code and deprecation fixes in ui_server.rs). No new issues. 3 previously flagged issues remain open.

  • src/config.rs tdd_load_toml_file only clears SNAPI_BASE before testing file-based config, but apply_env_overrides checks 6 env vars -- any of the others being set will silently override TOML values and break assertions
  • tests/config_integration.rs integration_load_json_config has the same env-isolation gap -- assertions on chain_id and snapi_base will fail if those env vars are set
  • src/chain.rs sequence retry loop re-uses the initial account_number without re-fetching -- under concurrent SDK usage the parsed expected sequence can go stale between retries
  • .github/scripts/e2e_pr.sh sn-api-server health check is a single curl after a fixed 3s sleep -- under set -euo pipefail a slow server start kills the entire E2E run; needs a retry loop
  • .github/scripts/e2e_public_pr.sh public endpoint smoke checks use bare curl -fsSL with no retries -- transient DNS/network blips against remote testnet endpoints will immediately abort the CI run under set -euo pipefail
  • examples/golden_devnet.rs default SNAPI_BASE fallback uses port 8089 but every other default in the codebase uses 8080 -- running the example without env vars connects to the wrong port
  • Cargo.toml axum, ripemd, and tower-http are listed under [dependencies] but only used by examples/ui_server.rs -- every downstream consumer transitively pulls in these crates; they should be [dev-dependencies]
  • examples/ui_server.rs auth_verify decodes the signature and checks its length but never cryptographically verifies it against the challenge message and public key -- any caller who knows a valid address+pubkey can forge authentication with an arbitrary 64+ byte blob
  • examples/ui_server.rs default SNAPI_BASE fallback uses port 8089 but the canonical default across the codebase is 8080 -- same inconsistency previously fixed in golden_devnet.rs
Previous reviews

Mention @roomote in a comment to request specific changes to this pull request or fix all unresolved issues.

@mateeullahmalik mateeullahmalik force-pushed the feat/readme-config-tests-hardening branch from ba24755 to fc84766 Compare March 17, 2026 10:24
@mateeullahmalik mateeullahmalik changed the title Harden Rust SDK integration surface: config, docs, and test coverage feat: deliver production-ready Rust SDK with public testnet sn-api E2E CI Mar 17, 2026
@mateeullahmalik mateeullahmalik changed the title feat: deliver production-ready Rust SDK with public testnet sn-api E2E CI Rust SDK with public testnet sn-api E2E CI Mar 17, 2026
@mateeullahmalik mateeullahmalik changed the title Rust SDK with public testnet sn-api E2E CI feat: deliver production-ready Rust SDK with public testnet sn-api E2E CI Mar 17, 2026
Comment on lines 14 to +41
@@ -20,7 +21,8 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "2"
rand = "0.8"
reqwest = { version = "0.12", features = ["json", "multipart", "stream", "rustls-tls"] }
ripemd = "0.1"
reqwest = { version = "0.12", default-features = false, features = ["json", "multipart", "stream", "rustls-tls"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "fs"] }
tempfile = "3"
prost = "0.13"
@@ -33,7 +35,10 @@ hex = "0.4"
bip39 = "2"
bip32 = "0.5"
chrono = { version = "0.4", default-features = false, features = ["clock"] }
dotenvy = "0.15"
toml = "0.8"
rq-library = { git = "https://github.com/LumeraProtocol/rq-library.git" }
tower-http = { version = "0.6", features = ["fs"] }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

axum, ripemd, and tower-http are only used by examples/ui_server.rs, but they're listed under [dependencies] instead of [dev-dependencies]. Cargo examples can use dev-dependencies, so placing them here means every downstream consumer of lumera-sdk-rs transitively pulls in axum, tower-http, and their dependency trees (hyper, multer, mime, etc.) even though they never use them. Move these three to [dev-dependencies] to keep the library's dependency footprint clean.

Fix it with Roo Code or mention @roomote and request a fix.

Comment on lines +293 to +306
let sig = B64.decode(body.signature.trim()).map_err(|e| ApiError {
error: format!("invalid signature base64: {e}"),
})?;
if sig.len() < 64 {
return Err(ApiError {
error: "invalid signature length".into(),
});
}

if challenge.message.is_empty() {
return Err(ApiError {
error: "invalid challenge message".into(),
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auth_verify decodes the signature bytes and checks sig.len() < 64, but never actually verifies the signature cryptographically against the challenge message and public key. After this check the function unconditionally issues a session token. This means any caller who knows a valid lumera address and its public key can authenticate by submitting an arbitrary 64+ byte blob as the signature, bypassing the entire challenge-response scheme. The signature should be verified using k256::ecdsa::VerifyingKey (or equivalent) against the Cosmos ADR-036 sign-bytes derived from challenge.message before issuing a token.

Fix it with Roo Code or mention @roomote and request a fix.

let rest = std::env::var("LUMERA_REST").unwrap_or_else(|_| "http://127.0.0.1:1317".into());
let rpc = std::env::var("LUMERA_RPC").unwrap_or_else(|_| "http://127.0.0.1:26657".into());
let grpc = std::env::var("LUMERA_GRPC").unwrap_or_else(|_| "http://127.0.0.1:9090".into());
let snapi = std::env::var("SNAPI_BASE").unwrap_or_else(|_| "http://127.0.0.1:8089".into());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default SNAPI_BASE here is port 8089, but the canonical default everywhere else in the codebase is 8080 (src/config.rs SdkSettings::default(), examples/golden_devnet.rs, scripts/run_golden_local.sh, .github/scripts/e2e_pr.sh). This is the same class of inconsistency that was previously flagged and fixed in golden_devnet.rs. Running the UI server without setting SNAPI_BASE will silently connect to the wrong port.

Suggested change
let snapi = std::env::var("SNAPI_BASE").unwrap_or_else(|_| "http://127.0.0.1:8089".into());
let snapi = std::env::var("SNAPI_BASE").unwrap_or_else(|_| "http://127.0.0.1:8080".into());

Fix it with Roo Code or mention @roomote and request a fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant