Last Notes
how am i supposed to buy your merch
if you just gonna not receive
ghost town
and honestly, nip04 not that bad, but only if it were to use fucking 10050 relay lists... otherwise, just gonna get ghosted
what do you mean? your ex husband didn't allow you to go grocery pickup or bubble tea pickup? did you marry a muslim extremist or something? I'm confused.
Yeap, but good for the PL focus
Looks like you both can get a 🍻 to forget about it
[Review] Bitcoin Beans La Reserva Cacao
by @nprofile…af8k in ~food_and_drinks, ~AGORA, ~HealthAndFitness
727 sats and 1 comment so far
https://stacker.news/items/1465134
I will keep using nip-04, I want to see who everyone is messaging!
That's crazy, 50mb is a 5 minutes video 😂
You can comment. People can like.
zero fks we’re given
for a brighter tomorrow ☀️
https://blossom.primal.net/58a9b1e52f31fc30d9a519973a0323754ba8e5f33d76fabe523413bfc309f6b8.jpg
Block 943674
1 - high priority
1 - medium priority
1 - low priority
1 - no priority
1 - purging
#bitcoinfees #mempool
It’s just the way it goes https://file.nostrmedia.com/p/d49a9023a21dba1b3c8306ca369bf3243d8b44b8f0b6d1196607f7b0990fa8df/fde16a38f2304a285307a65580580a76ace7bc25cfe7a864aaa5b942db810779.png
dont listen to @nprofile…mf6p he wanta to mine but is a fiat maxi
Next up a button to name & shame. Start a hellthread with the 20 biggest avatars 🚀😂
Beef n Sausage Spaghetti for dinner tonight
https://image.nostr.build/a30385a770fc6c29ea81fa679ae59ea99dc8d160bc0e311eaeb9356d6fdf88be.jpg
I noticed it when I made Flappy Nostrich, using avatars for in game elements. The biggest pain of the whole thing was making avatars work.
true, lots of toppings to try.
Plenty of woke nostr and core devs will bend the knee when pressed. Wokeness is synonymous with complying with authority.
[package]
name = "n34-relay"
description = "A nostr GRASP relay implementation"
version = "0.1.0"
edition = "2024"
license = "AGPL-3.0-or-later"
authors = ["Awiteb <
[email protected]>"]
readme = "README.md"
repository = "https://github.com/gnostr-org/get_file_hash.git"
documentation = "https://relay.n34.dev/docs.html"
homepage = "https://relay.n34.dev"
keywords = ["nostr", "NIP-34", "GRASP"]
rust-version = "1.88.0"
[package.metadata.wix]
upgrade-guid = "036A2A4F-DA54-4FE2-B2D8-8C58D5814874"
path-guid = "97D3207D-71DB-4DD0-B548-E43E8C664B7D"
license = false
eula = false
documentation = "https://relay.n34.dev/docs.html"
homepage = "https://relay.n34.dev"
[[bin]]
name = "n34-relay"
path = "src/main.rs"
[dependencies]
rhai = { version = "1.23.4", features = [
"no_position",
"sync",
"serde",
"decimal",
] }
tokio = { version = "1.47.1", features = [
"macros",
"rt-multi-thread",
"signal",
"fs",
"process",
] }
tonic = { version = "0.14.2", features = [
"tls-ring",
"tls-webpki-roots",
"gzip",
"deflate",
] }
tower-http = { version = "0.6.6", features = [
"cors",
"decompression-br",
"decompression-deflate",
"decompression-gzip",
"decompression-zstd",
"trace",
"timeout",
] }
axum = { version = "0.8.6", features = ["http2", "ws"] }
base64 = "0.22.1"
chrono = "0.4.42"
config = { version = "0.15.15", default-features = false, features = ["toml"] }
const_format = "0.2.34"
convert_case = "0.8.0"
easy-ext = "1.0.2"
either = "1.15.0"
flume = "0.11.1"
futures = "0.3.31"
hyper = "1.7.0"
hyper-util = "0.1.17"
parking_lot = { version = "0.12.5", features = ["serde"] }
prost = "0.14.1"
serde = { version = "1.0.219", features = ["rc"] }
serde_json = "1.0.145"
serde_with = "3.15.0"
sha1 = "0.10.6"
sha2 = "0.10.9"
strum = { version = "0.27.2", features = ["derive"] }
thiserror = "2.0.16"
tokio-util = { version = "0.7.17", features = ["io"] }
toml = "0.9.5"
tonic-prost = "0.14.2"
tower = { version = "0.5.2", features = ["limit"] }
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
dirs = "6.0.0"
# We frequently switch between stable and unstable versions; this will make the
# process easier.
[dependencies.nostr]
default-features = false
features = ["std"]
git = "https://git.4rs.nl/mirrors/nostr.git"
rev = "27a1947d3"
# version = "0.45.0"
[dependencies.nostr-database]
default-features = false
git = "https://git.4rs.nl/mirrors/nostr.git"
rev = "27a1947d3"
# version = "0.45.0"
[dependencies.nostr-lmdb]
default-features = false
git = "https://git.4rs.nl/mirrors/nostr.git"
rev = "27a1947d3"
# version = "0.45.0"
[dependencies.nostr-relay-builder]
default-features = false
git = "https://git.4rs.nl/mirrors/nostr.git"
rev = "27a1947d3"
# version = "0.45.0"
[build-dependencies]
tonic-prost-build = "0.14.2"
# [profile.release]
# lto = "fat"
# opt-level = 3
i would be tempted to jab
exclude = ["target/**/*.toml", "Cargo.lock"]
[formatting]
align_entries = true
indent_tables = true
reorder_keys = false
[[rule]]
include = ["**/Cargo.toml"]
keys = ["dependencies"]
formatting.reorder_keys = true
https://media.tenor.com/I3dqkfMwImcAAAAC/harry-enfield-frank-doberman.gif
/lmdb
/target
/relay_base
.rustc_info.json
bin/CACHEDIR.TAG
bin/config.toml
bin/debug/
bin/lmdb/
bin/logs.log
#!/usr/bin/env sh
N34_RELAY_BASE_DIR=./bin RUST_LOG=debug cargo run -p n34-relay
#[cfg(feature = "nostr")]
use frost_secp256k1_tr as frost;
#[cfg(feature = "nostr")]
use rand::thread_rng;
#[cfg(feature = "nostr")]
use std::collections::BTreeMap;
#[cfg(feature = "nostr")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut rng = thread_rng();
let max_signers = 3;
let min_signers = 2;
////////////////////////////////////////////////////////////////////////////
// Round 0: Key Generation (Trusted Dealer)
////////////////////////////////////////////////////////////////////////////
// In a real P2P setup, you'd use Distributed Key Generation (DKG).
// For local testing/simulations, the trusted dealer is faster.
let (shares, pubkey_package) = frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
&mut rng,
)?;
// Verifying the public key exists
let group_public_key = pubkey_package.verifying_key();
println!("Group Public Key: {:?}", group_public_key);
////////////////////////////////////////////////////////////////////////////
// Round 1: Commitment
////////////////////////////////////////////////////////////////////////////
let message = b"BIP-64MOD Consensus Proposal";
let mut signing_commitments = BTreeMap::new();
let mut participant_nonces = BTreeMap::new();
// Participants 1 and 2 decide to sign
for i in 1..=min_signers {
let identifier = frost::Identifier::try_from(i as u16)?;
// Generate nonces and commitments
let (nonces, commitments) = frost::round1::commit(
shares[&identifier].signing_share(),
&mut rng,
);
signing_commitments.insert(identifier, commitments);
participant_nonces.insert(identifier, nonces);
}
////////////////////////////////////////////////////////////////////////////
// Round 2: Signing
////////////////////////////////////////////////////////////////////////////
let mut signature_shares = BTreeMap::new();
let signing_package = frost::SigningPackage::new(signing_commitments, message);
for i in 1..=min_signers {
let identifier = frost::Identifier::try_from(i as u16)?;
let nonces = &participant_nonces[&identifier];
// Each participant produces a signature share
let key_package: frost::keys::KeyPackage = shares[&identifier].clone().try_into()?;
let share = frost::round2::sign(&signing_package, nonces, &key_package)?;
signature_shares.insert(identifier, share);
}
////////////////////////////////////////////////////////////////////////////
// Finalization: Aggregation
////////////////////////////////////////////////////////////////////////////
let group_signature = frost::aggregate(
&signing_package,
&signature_shares,
&pubkey_package,
)?;
// Verification
group_public_key.verify(message, &group_signature)?;
println!("Threshold signature verified successfully!");
Ok(())
}
#[cfg(not(feature = "nostr"))]
fn main() {
println!("This example requires the 'nostr' feature. Please run with: cargo run --example trusted-dealer --features nostr");
}
#[cfg(feature = "nostr")]
use frost_secp256k1_tr as frost;
#[cfg(feature = "nostr")]
use rand::thread_rng;
#[cfg(feature = "nostr")]
use std::collections::BTreeMap;
/// A simplified ROAST Coordinator that manages signing sessions
#[cfg(feature = "nostr")]
struct RoastCoordinator {
min_signers: u16,
_message: Vec<u8>,
commitments: BTreeMap<frost::Identifier, frost::round1::SigningCommitments>,
nonces: BTreeMap<frost::Identifier, frost::round1::SigningNonces>,
shares: BTreeMap<frost::Identifier, frost::round2::SignatureShare>,
}
#[cfg(feature = "nostr")]
impl RoastCoordinator {
fn new(min_signers: u16, message: &[u8]) -> Self {
Self {
min_signers,
_message: message.to_vec(),
commitments: BTreeMap::new(),
nonces: BTreeMap::new(),
shares: BTreeMap::new(),
}
}
/// ROAST Logic: Collect commitments until we hit the threshold.
/// In a real P2P system, this would be an async stream handler.
fn add_commitment(&mut self, id: frost::Identifier, comms: frost::round1::SigningCommitments, nonces: frost::round1::SigningNonces) {
if self.commitments.len() < self.min_signers as usize {
self.commitments.insert(id, comms);
self.nonces.insert(id, nonces);
}
}
/// ROAST Logic: Collect signature shares.
fn add_share(&mut self, id: frost::Identifier, share: frost::round2::SignatureShare) {
if self.shares.len() < self.min_signers as usize {
self.shares.insert(id, share);
}
}
fn is_ready_to_sign(&self) -> bool {
self.commitments.len() >= self.min_signers as usize
}
fn is_ready_to_aggregate(&self) -> bool {
self.shares.len() >= self.min_signers as usize
}
}
#[cfg(feature = "nostr")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut rng = thread_rng();
let (max_signers, min_signers) = (5, 3);
let message = b"BIP-64MOD Context: ROAST Coordination";
// 1. Setup: Generate keys (Dealer mode for simulation)
let (key_shares, pubkey_package) = frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
&mut rng,
)?;
let mut coordinator = RoastCoordinator::new(min_signers, message);
// 2. Round 1: Asynchronous Commitment Collection
// Simulate signers 1, 3, and 5 responding first (ROAST skips 2 and 4)
for &id_num in &[1, 3, 5] {
let id = frost::Identifier::try_from(id_num as u16)?;
let (nonces, comms) = frost::round1::commit(key_shares[&id].signing_share(), &mut rng);
// Signers store their nonces locally, send comms to coordinator
coordinator.add_commitment(id, comms, nonces);
// Note: Signer 2 was "offline", but ROAST doesn't care because we hit 3/5.
}
// 3. Round 2: Signing
if coordinator.is_ready_to_sign() {
let signing_package = frost::SigningPackage::new(coordinator.commitments.clone(), message);
let mut temp_shares = BTreeMap::new();
for &id in coordinator.commitments.keys() {
// In reality, coordinator sends signing_package to signers
// Here we simulate the signers producing shares
let nonces = &coordinator.nonces[&id];
let key_package: frost::keys::KeyPackage = key_shares[&id].clone().try_into()?;
let share = frost::round2::sign(&signing_package, &nonces, &key_package)?;
temp_shares.insert(id, share);
}
for (id, share) in temp_shares {
coordinator.add_share(id, share);
}
}
// 4. Finalization: Aggregation
if coordinator.is_ready_to_aggregate() {
let signing_package = frost::SigningPackage::new(coordinator.commitments.clone(), message);
let group_signature = frost::aggregate(
&signing_package,
&coordinator.shares,
&pubkey_package,
)?;
pubkey_package.verifying_key().verify(message, &group_signature)?;
println!("ROAST-coordinated signature verified!");
}
Ok(())
}
#[cfg(not(feature = "nostr"))]
fn main() {
println!("This example requires the 'nostr' feature. Please run with: cargo run --example roast-experiment --features nostr");
}
#[tokio::main]
#[cfg(feature = "nostr")]
#[allow(unused_imports)]
async fn main() {
use get_file_hash_core::repository_announcement;
use get_file_hash_core::get_file_hash;
use nostr_sdk::Keys;
use sha2::{Digest, Sha256};
use nostr_sdk::EventId;
use std::str::FromStr;
let keys = Keys::generate();
let relay_urls = get_file_hash_core::get_relay_urls();
let project_name = "my-awesome-repo-example";
let description = "A fantastic new project example.";
let clone_url = "
[email protected]:user/my-awesome-repo-example.git";
// Dummy EventId for examples that require a build_manifest_event_id
const DUMMY_BUILD_MANIFEST_ID_STR: &str = "f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0";
let dummy_build_manifest_id = EventId::from_str(DUMMY_BUILD_MANIFEST_ID_STR).unwrap();
// Example 1: Without build_manifest_event_id
println!("Publishing repository announcement without build_manifest_event_id...");
repository_announcement!(
&keys,
&relay_urls,
project_name,
description,
clone_url,
"../Cargo.toml" // Use a known file in your project
);
println!("Repository announcement without build_manifest_event_id published.");
// Example 2: With build_manifest_event_id
println!("Publishing repository announcement with build_manifest_event_id...");
repository_announcement!(
&keys,
&relay_urls,
project_name,
description,
clone_url,
"../Cargo.toml", // Use a known file in your project
Some(&dummy_build_manifest_id)
);
println!("Repository announcement with build_manifest_event_id published.");
}
#[cfg(not(feature = "nostr"))]
fn main() {
println!("This example requires the 'nostr' feature. Please run with: cargo run --example repository_announcement --features nostr");
}
Howdy! @Undisciplined just posted [Review] Bitcoin Beans La Reserva Cacao in the #AGORA, a #Bitcoin #P2P #Marketplace. Check it out https://stacker.news/items/1465134/r/AG0RA #forsale #shopstr
#[tokio::main]
#[cfg(feature = "nostr")]
async fn main() {
use get_file_hash_core::publish_repository_state;
use nostr_sdk::Keys;
let keys = Keys::generate();
let relay_urls = get_file_hash_core::get_relay_urls();
let d_tag = "my-awesome-repo-example";
let branch_name = "main";
let commit_id = "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0";
println!("Publishing repository state...");
publish_repository_state!(
&keys,
&relay_urls,
d_tag,
branch_name,
commit_id
);
println!("Repository state published.");
}
#[cfg(not(feature = "nostr"))]
fn main() {
println!("This example requires the 'nostr' feature. Please run with: cargo run --example publish_repository_state --features nostr");
}
#[tokio::main]
#[cfg(feature = "nostr")]
async fn main() {
use get_file_hash_core::publish_pull_request;
use nostr_sdk::Keys;
use nostr_sdk::EventId;
use std::str::FromStr;
let keys = Keys::generate();
let relay_urls = get_file_hash_core::get_relay_urls();
let d_tag = "my-awesome-repo-example";
let commit_id = "0123456789abcdef0123456789abcdef01234567";
let clone_url = "
[email protected]:user/my-feature-branch.git";
let title = Some("Feat: Add new awesome feature example");
// Dummy EventId for examples that require a build_manifest_event_id
const DUMMY_BUILD_MANIFEST_ID_STR: &str = "f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0";
let dummy_build_manifest_id = EventId::from_str(DUMMY_BUILD_MANIFEST_ID_STR).unwrap();
// Example 1: Without title and build_manifest_event_id
println!("Publishing pull request without title and build_manifest_event_id...");
publish_pull_request!(
&keys,
&relay_urls,
d_tag,
commit_id,
clone_url
);
println!("Pull request without title and build_manifest_event_id published.");
// Example 2: With title but without build_manifest_event_id
println!("Publishing pull request with title but without build_manifest_event_id...");
publish_pull_request!(
&keys,
&relay_urls,
d_tag,
commit_id,
clone_url,
title
);
println!("Pull request with title but without build_manifest_event_id published.");
// Example 3: With build_manifest_event_id but without title
println!("Publishing pull request with build_manifest_event_id but without title...");
publish_pull_request!(
&keys,
&relay_urls,
d_tag,
commit_id,
clone_url,
None, // Explicitly pass None for title
Some(&dummy_build_manifest_id)
);
println!("Pull request with build_manifest_event_id but without title published.");
// Example 4: With title and build_manifest_event_id
println!("Publishing pull request with title and build_manifest_event_id...");
publish_pull_request!(
&keys,
&relay_urls,
d_tag,
commit_id,
clone_url,
title,
Some(&dummy_build_manifest_id)
);
println!("Pull request with title and build_manifest_event_id published.");
}
#[cfg(not(feature = "nostr"))]
fn main() {
println!("This example requires the 'nostr' feature. Please run with: cargo run --example publish_pull_request --features nostr");
}
#[tokio::main]
#[cfg(feature = "nostr")]
async fn main() {
use get_file_hash_core::publish_pr_update;
use nostr_sdk::Keys;
use nostr_sdk::EventId;
use std::str::FromStr;
let keys = Keys::generate();
let relay_urls = get_file_hash_core::get_relay_urls();
let d_tag = "my-awesome-repo-example";
let pr_event_id = EventId::from_str("f6e4d6a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9").unwrap(); // Example PR Event ID
let updated_commit_id = "z9y8x7w6v5u4t3s2r1q0p9o8n7m6l5k4j3i2h1g0";
let updated_clone_url = "
[email protected]:user/my-feature-branch-v2.git";
// Dummy EventId for examples that require a build_manifest_event_id
const DUMMY_BUILD_MANIFEST_ID_STR: &str = "f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0";
let dummy_build_manifest_id = EventId::from_str(DUMMY_BUILD_MANIFEST_ID_STR).unwrap();
// Example 1: Without build_manifest_event_id
println!("Publishing PR update without build_manifest_event_id...");
publish_pr_update!(
&keys,
&relay_urls,
d_tag,
&pr_event_id,
updated_commit_id,
updated_clone_url
);
println!("PR update without build_manifest_event_id published.");
// Example 2: With build_manifest_event_id
println!("Publishing PR update with build_manifest_event_id...");
publish_pr_update!(
&keys,
&relay_urls,
d_tag,
&pr_event_id,
updated_commit_id,
updated_clone_url,
Some(&dummy_build_manifest_id)
);
println!("PR update with build_manifest_event_id published.");
}
#[cfg(not(feature = "nostr"))]
fn main() {
println!("This example requires the 'nostr' feature. Please run with: cargo run --example publish_pr_update --features nostr");
}
#[cfg(feature = "nostr")]
use get_file_hash_core::{get_relay_urls, publish_patch, publish_metadata_event, DEFAULT_PICTURE_URL, DEFAULT_BANNER_URL};
#[cfg(feature = "nostr")]
#[tokio::main]
async fn main() {
use nostr_sdk::Keys;
use nostr_sdk::EventId;
use std::str::FromStr;
let keys = Keys::generate();
let relay_urls = get_relay_urls();
let d_tag = "my-gnostr-repository-patch-with-metadata-example"; // Repository identifier
let commit_id = "f1e2d3c4b5a6f7e8d9c0b1a2f3e4d5c6b7a8f9e0"; // Example commit ID
// Metadata for NIP-01 event
let picture_url = DEFAULT_PICTURE_URL;
let banner_url = DEFAULT_BANNER_URL;
let metadata_file_path = "./README.md"; // Using README.md content for metadata
// Dummy EventId for examples that require a build_manifest_event_id
const DUMMY_BUILD_MANIFEST_ID_STR: &str = "f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0";
let dummy_build_manifest_id = EventId::from_str(DUMMY_BUILD_MANIFEST_ID_STR).unwrap();
println!("Publishing NIP-01 Metadata Event...");
publish_metadata_event(
&keys,
&relay_urls,
picture_url,
banner_url,
metadata_file_path
).await;
println!("NIP-01 Metadata Event published.");
println!("
Publishing NIP-34 Patch Event without build_manifest_event_id...");
publish_patch!(
&keys,
&relay_urls,
d_tag,
commit_id,
"../Cargo.toml" // Use an existing file for the patch content
);
println!("NIP-34 Patch Event without build_manifest_event_id published.");
println!("
Publishing NIP-34 Patch Event with build_manifest_event_id...");
publish_patch!(
&keys,
&relay_urls,
d_tag,
commit_id,
"../Cargo.toml", // Use an existing file for the patch content
Some(&dummy_build_manifest_id)
);
println!("NIP-34 Patch Event with build_manifest_event_id published.");
}
#[cfg(not(feature = "nostr"))]
fn main() {
println!("This example requires the 'nostr' feature. Please run with: cargo run --example publish_patch_with_metadata --features nostr");
}
#[cfg(feature = "nostr")]
use get_file_hash_core::{get_git_tracked_files, DEFAULT_GNOSTR_KEY, DEFAULT_PICTURE_URL, DEFAULT_BANNER_URL, publish_nostr_event_if_release, get_repo_announcement_event, publish_patch_event};
#[cfg(feature = "nostr")]
#[tokio::main]
async fn main() {
use get_file_hash_core::publish_patch;
use nostr_sdk::Keys;
use nostr_sdk::EventId;
use std::str::FromStr;
let keys = Keys::generate();
let relay_urls = get_file_hash_core::get_relay_urls();
let d_tag = "my-awesome-repo-example";
let commit_id = "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0"; // Example commit ID
// Dummy EventId for examples that require a build_manifest_event_id
const DUMMY_BUILD_MANIFEST_ID_STR: &str = "f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0";
let dummy_build_manifest_id = EventId::from_str(DUMMY_BUILD_MANIFEST_ID_STR).unwrap();
// Example 1: Without build_manifest_event_id
println!("Publishing patch without build_manifest_event_id...");
publish_patch!(
&keys,
&relay_urls,
d_tag,
commit_id,
"../Cargo.toml" // Use an existing file for the patch content
);
println!("Patch without build_manifest_event_id published.");
// Example 2: With build_manifest_event_id
println!("Publishing patch with build_manifest_event_id...");
publish_patch!(
&keys,
&relay_urls,
d_tag,
commit_id,
"../Cargo.toml", // Use an existing file for the patch content
Some(&dummy_build_manifest_id)
);
println!("Patch with build_manifest_event_id published.");
}
#[cfg(not(feature = "nostr"))]
fn main() {
println!("This example requires the 'nostr' feature. Please run with: cargo run --example publish_patch --features nostr");
}
#[cfg(feature = "nostr")]
use get_file_hash_core::{get_relay_urls, publish_issue, DEFAULT_GNOSTR_KEY, DEFAULT_PICTURE_URL, DEFAULT_BANNER_URL, publish_nostr_event_if_release, get_repo_announcement_event, publish_patch_event};
#[cfg(feature = "nostr")]
#[tokio::main]
async fn main() {
use nostr_sdk::Keys;
use nostr_sdk::EventId;
use std::str::FromStr;
let keys = Keys::generate();
let relay_urls = get_relay_urls();
let d_tag = "my-gnostr-repository-issue-example"; // Repository identifier
let issue_id_1 = "issue-001"; // Unique identifier for the first issue
let issue_id_2 = "issue-002"; // Unique identifier for the second issue
let title_1 = "Bug: Application crashes on startup";
let content_1 = "The application fails to launch on macOS Ventura. It throws a 'Segmentation Fault' error immediately after execution. This was observed on version `v1.2.3`.
Steps to reproduce:
1. Download `app-v1.2.3-macos.tar.gz`
2. Extract the archive
3. Run `./app`
Expected behavior: Application launches successfully.
Actual behavior: Application crashes with 'Segmentation Fault'.";
let title_2 = "Feature Request: Dark Mode";
let content_2 = "Users have requested a dark mode option to improve readability and reduce eye strain during prolonged use. This should be toggleable in the settings menu.
Considerations:
- Adherence to system dark mode settings.
- Consistent styling across all UI components.";
// Dummy EventId for examples that require a build_manifest_event_id
const DUMMY_BUILD_MANIFEST_ID_STR: &str = "f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0";
let dummy_build_manifest_id = EventId::from_str(DUMMY_BUILD_MANIFEST_ID_STR).unwrap();
// Example 1: Publish an issue without build_manifest_event_id
println!("Publishing issue '{}' without build_manifest_event_id...", title_1);
publish_issue!(
&keys,
&relay_urls,
d_tag,
issue_id_1,
title_1,
content_1
);
println!("Issue '{}' published.", title_1);
// Example 2: Publish an issue with build_manifest_event_id
println!("Publishing issue '{}' with build_manifest_event_id...", title_2);
publish_issue!(
&keys,
&relay_urls,
d_tag,
issue_id_2,
title_2,
content_2,
Some(&dummy_build_manifest_id)
);
println!("Issue '{}' published.", title_2);
}
#[cfg(not(feature = "nostr"))]
fn main() {
println!("This example requires the 'nostr' feature. Please run with: cargo run --example publish_issue --features nostr");
}
/// deterministic nostr event build example
// deterministic nostr event build example
use get_file_hash_core::get_file_hash;
#[cfg(all(not(debug_assertions), feature = "nostr"))]
use get_file_hash_core::{get_git_tracked_files, DEFAULT_GNOSTR_KEY, DEFAULT_PICTURE_URL, DEFAULT_BANNER_URL, publish_nostr_event_if_release, get_repo_announcement_event};
#[cfg(all(not(debug_assertions), feature = "nostr"))]
use nostr_sdk::{EventBuilder, Keys, Tag, SecretKey};
#[cfg(all(not(debug_assertions), feature = "nostr"))]
use std::fs;
use std::path::PathBuf;
use sha2::{Digest, Sha256};
#[cfg(all(not(debug_assertions), feature = "nostr"))]
use ::hex;
#[tokio::main]
async fn main() {
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
let is_git_repo = std::path::Path::new(&manifest_dir).join(".git").exists();
#[cfg(all(not(debug_assertions), feature = "nostr"))]
#[allow(unused_mut)]
let mut git_branch_str = String::new();
println!("cargo:rustc-env=CARGO_PKG_NAME={}", env!("CARGO_PKG_NAME"));
println!("cargo:rustc-env=CARGO_PKG_VERSION={}", env!("CARGO_PKG_VERSION"));
if is_git_repo {
let git_commit_hash_output = std::process::Command::new("git")
.args(&["rev-parse", "HEAD"])
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.output()
.expect("Failed to execute git command for commit hash");
let git_commit_hash_str = if git_commit_hash_output.status.success() && !git_commit_hash_output.stdout.is_empty() {
String::from_utf8(git_commit_hash_output.stdout).unwrap().trim().to_string()
} else {
println!("cargo:warning=Git commit hash command failed or returned empty. Status: {:?}, Stderr: {}",
git_commit_hash_output.status, String::from_utf8_lossy(&git_commit_hash_output.stderr));
String::new()
};
println!("cargo:rustc-env=GIT_COMMIT_HASH={}", git_commit_hash_str);
let git_branch_output = std::process::Command::new("git")
.args(&["rev-parse", "--abbrev-ref", "HEAD"])
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.output()
.expect("Failed to execute git command for branch name");
let git_branch_str = if git_branch_output.status.success() && !git_branch_output.stdout.is_empty() {
String::from_utf8(git_branch_output.stdout).unwrap().trim().to_string()
} else {
println!("cargo:warning=Git branch command failed or returned empty. Status: {:?}, Stderr: {}",
git_branch_output.status, String::from_utf8_lossy(&git_branch_output.stderr));
String::new()
};
println!("cargo:rustc-env=GIT_BRANCH={}", git_branch_str);
} else {
println!("cargo:rustc-env=GIT_COMMIT_HASH=");
println!("cargo:rustc-env=GIT_BRANCH=");
}
println!("cargo:rerun-if-changed=.git/HEAD");
//#[cfg(all(not(debug_assertions), feature = "nostr"))]
//let relay_urls = get_file_hash_core::get_relay_urls();
let cargo_toml_hash = get_file_hash!("../Cargo.toml");
println!("cargo:rustc-env=CARGO_TOML_HASH={}", cargo_toml_hash);
let lib_hash = get_file_hash!("../src/lib.rs");
println!("cargo:rustc-env=LIB_HASH={}", lib_hash);
let build_hash = get_file_hash!("../build.rs");
println!("cargo:rustc-env=BUILD_HASH={}", build_hash);
println!("cargo:rerun-if-changed=Cargo.toml");
println!("cargo:rerun-if-changed=src/lib.rs");
println!("cargo:rerun-if-changed=build.rs");
let online_relays_csv_path = PathBuf::from(&manifest_dir).join("src/get_file_hash_core/src/online_relays_gps.csv");
if online_relays_csv_path.exists() {
println!("cargo:rerun-if-changed={}", online_relays_csv_path.to_str().unwrap());
}
#[cfg(all(not(debug_assertions), feature = "nostr"))]
if cfg!(not(debug_assertions)) {
println!("cargo:warning=Nostr feature enabled: Build may take longer due to network operations (publishing events to relays).");
// This code only runs in release builds
let package_version = std::env::var("CARGO_PKG_VERSION").unwrap();
let output_dir = PathBuf::from(format!(".gnostr/build/{}", package_version));
if let Err(e) = fs::create_dir_all(&output_dir) {
println!("cargo:warning=Failed to create output directory {}: {}", output_dir.display(), e);
}
let files_to_publish: Vec<String> = get_git_tracked_files(&PathBuf::from(&manifest_dir));
let git_commit_hash_output = std::process::Command::new("git")
.args(&["rev-parse", "HEAD"])
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.output()
.expect("Failed to execute git command for commit hash");
let git_commit_hash_str = if git_commit_hash_output.status.success() && !git_commit_hash_output.stdout.is_empty() {
String::from_utf8(git_commit_hash_output.stdout).unwrap().trim().to_string()
} else {
println!("cargo:warning=Git commit hash command failed or returned empty. Status: {:?}, Stderr: {}",
git_commit_hash_output.status, String::from_utf8_lossy(&git_commit_hash_output.stderr));
String::new()
};
println!("cargo:rustc-env=GIT_COMMIT_HASH={}", git_commit_hash_str);
// Create padded_commit_hash
let padded_commit_hash = format!("{:0>64}", &git_commit_hash_str);
println!("cargo:rustc-env=PADDED_COMMIT_HASH={}", padded_commit_hash);
// Initialize client and keys once
let initial_secret_key = SecretKey::parse(&padded_commit_hash).expect("Failed to create Nostr SecretKey from PADDED_COMMIT_HASH");
let initial_keys = Keys::new(initial_secret_key);
let mut client = nostr_sdk::Client::new(initial_keys.clone());
let mut relay_urls = get_file_hash_core::get_relay_urls();
// Add relays to the client
for relay_url in relay_urls.iter() {
if let Err(e) = client.add_relay(relay_url).await {
println!("cargo:warning=Failed to add relay {}: {}", relay_url, e);
}
}
client.connect().await;
println!("cargo:warning=Added and connected to {} relays.", relay_urls.len());
let mut published_event_ids: Vec<Tag> = Vec::new();
let mut total_bytes_sent: usize = 0;
for file_path_str in &files_to_publish {
println!("cargo:warning=Processing file: {}", file_path_str);
match fs::read(file_path_str) {
Ok(bytes) => {
let mut hasher = Sha256::new();
hasher.update(&bytes);
let result = hasher.finalize();
let file_hash_hex = hex::encode(result);
match SecretKey::from_hex(&file_hash_hex.clone()) {
Ok(secret_key) => {
let keys = Keys::new(secret_key);
let content = String::from_utf8_lossy(&bytes).into_owned();
let tags = vec![
Tag::parse(["file", file_path_str].iter().map(ToString::to_string).collect::<Vec<String>>()).unwrap(),
Tag::parse(["version", &package_version].iter().map(ToString::to_string).collect::<Vec<String>>()).unwrap(),
];
let event_builder = EventBuilder::text_note(content).tags(tags);
if let Some(event_id) = publish_nostr_event_if_release(&mut client, file_hash_hex, keys.clone(), event_builder, &mut relay_urls, file_path_str, &output_dir, &mut total_bytes_sent).await {
published_event_ids.push(Tag::event(event_id));
}
// Publish metadata event
get_file_hash_core::publish_metadata_event(
&keys,
&relay_urls,
DEFAULT_PICTURE_URL,
DEFAULT_BANNER_URL,
file_path_str,
).await;
}
Err(e) => {
println!("cargo:warning=Failed to derive Nostr secret key for {}: {}", file_path_str, e);
}
}
}
Err(e) => {
println!("cargo:warning=Failed to read file {}: {}", file_path_str, e);
}
}
}
// Create and publish the build_manifest
if !published_event_ids.is_empty() {
//TODO this will be either the default or detected from env vars PRIVATE_KEY
let keys = Keys::new(SecretKey::from_hex(DEFAULT_GNOSTR_KEY).expect("Failed to create Nostr keys from DEFAULT_GNOSTR_KEY"));
let cloned_keys = keys.clone();
let content = format!("Build manifest for get_file_hash v{}", package_version);
let mut tags = vec![
Tag::parse(["build_manifest", &package_version].iter().map(ToString::to_string).collect::<Vec<String>>()).unwrap(),
Tag::parse(["build_manifest", &package_version].iter().map(ToString::to_string).collect::<Vec<String>>()).unwrap(),
Tag::parse(["build_manifest", &package_version].iter().map(ToString::to_string).collect::<Vec<String>>()).unwrap(),
Tag::parse(["build_manifest", &package_version].iter().map(ToString::to_string).collect::<Vec<String>>()).unwrap(),
];
tags.extend(published_event_ids);
let event_builder = EventBuilder::text_note(content.clone()).tags(tags);
if let Some(event_id) = publish_nostr_event_if_release(
&mut client,
hex::encode(Sha256::digest(content.as_bytes())),
keys,
event_builder,
&mut relay_urls,
"build_manifest.json",
&output_dir,
&mut total_bytes_sent,
).await {
let build_manifest_event_id = Some(event_id);
// Publish metadata event for the build manifest
get_file_hash_core::publish_metadata_event(
&cloned_keys, // Use reference to cloned keys here
&relay_urls,
DEFAULT_PICTURE_URL,
DEFAULT_BANNER_URL,
&format!("build_manifest:{}", package_version),
).await;
let git_commit_hash = &git_commit_hash_str;
let git_branch = &git_branch_str;
let repo_url = std::env::var("CARGO_PKG_REPOSITORY").unwrap();
let repo_name = std::env::var("CARGO_PKG_NAME").unwrap();
let repo_description = std::env::var("CARGO_PKG_DESCRIPTION").unwrap();
let output_dir = PathBuf::from(format!(".gnostr/build/{}", package_version));
if let Err(e) = fs::create_dir_all(&output_dir) {
println!("cargo:warning=Failed to create output directory {}: {}", output_dir.display(), e);
}
let announcement_keys = Keys::new(SecretKey::from_hex(build_manifest_event_id.unwrap().to_hex().as_str()).expect("Failed to create Nostr keys from build_manifest_event_id"));
let announcement_pubkey_hex = announcement_keys.public_key().to_string();
// Publish NIP-34 Repository Announcement
if let Some(_event_id) = get_repo_announcement_event(
&mut client,
&announcement_keys,
&relay_urls,
&repo_url,
&repo_name,
&repo_description,
&git_commit_hash,
&git_branch,
&output_dir,
&announcement_pubkey_hex
).await {
// Successfully published announcement
}
}
}
println!("cargo:warning=Total bytes sent to Nostr relays: {} bytes ({} MB)", total_bytes_sent, total_bytes_sent as f64 / 1024.0 / 1024.0);
}
}
// deterministic nostr event build example
/// BIP-64MOD + GCC: Complete Git Empty & Genesis Constants
///
/// This module provides the standard cryptographic identifiers for "null",
/// "empty", and "genesis" states, including NIP-19 (Bech32) identities.
pub struct GitEmptyState;
impl GitEmptyState {
// === NULL REFERENCE (Zero Hash) ===
pub const NULL_SHA256: &'static str = "0000000000000000000000000000000000000000000000000000000000000000";
// === EMPTY BLOB (Empty File) ===
pub const BLOB_SHA1: &'static str = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391";
pub const BLOB_SHA256: &'static str = "473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813";
pub const BLOB_NSEC: &'static str = "nsec1guaq7npmaz5ndqdzvl3mr6d8mndprp2rdls5ram5jys2xqmjrqfsdzhrp6";
pub const BLOB_NPUB: &'static str = "npub180cvv07tjdrghvkyh6964p7w9vsqpf3p05868v399v86p8y6f69sq5fdp0";
// === EMPTY TREE (Empty Directory) ===
pub const TREE_SHA1: &'static str = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
pub const TREE_SHA256: &'static str = "6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321";
pub const TREE_NSEC: &'static str = "nsec1dmceksfzt3fknuwpqn29mrv9a75mq4a48v2tfwde88whfhkv2vsslsc46c";
pub const TREE_NPUB: &'static str = "npub1pxmpep6yk7z6p332u9588k0vscg26rv29pynvscg26rv29pynvsq6erdfh";
// === GENESIS COMMIT (DeepSpaceM1 @ Epoch 0) ===
/// Result of: git commit --allow-empty -m 'Initial commit'
/// With Author/Committer: DeepSpaceM1 <
[email protected]> @ 1970-01-01T00:00:00Z
pub const GENESIS_AUTHOR_NAME: &'static str = "DeepSpaceM1";
pub const GENESIS_AUTHOR_EMAIL: &'static str = "
[email protected]";
pub const GENESIS_DATE_UNIX: i64 = 0;
pub const GENESIS_MESSAGE: &'static str = "Initial commit";
/// The resulting SHA-256 Commit Hash for this specific configuration
pub const GENESIS_COMMIT_SHA256: &'static str = "e9768652d87e07663479a0ad402513f56d953930b659c2ef389d4d03d3623910";
/// The NIP-19 Identity associated with the Genesis Commit
pub const GENESIS_NSEC: &'static str = "nsec1jpxmpep6yk7z6p332u9588k0vscg26rv29pynvscg26rv29pynvsq68at9d";
pub const GENESIS_NPUB: &'static str = "npub1pxmpep6yk7z6p332u9588k0vscg26rv29pynvscg26rv29pynvsq6erdfh";
}
/// Helper for constructing the commit object string for hashing
pub mod builders {
use super::GitEmptyState;
pub fn build_genesis_commit_object() -> String {
format!(
"tree {}\nauthor {} <{}> {} +0000\ncommitter {} <{}> {} +0000\n\n{}\n",
GitEmptyState::TREE_SHA256,
GitEmptyState::GENESIS_AUTHOR_NAME,
GitEmptyState::GENESIS_AUTHOR_EMAIL,
GitEmptyState::GENESIS_DATE_UNIX,
GitEmptyState::GENESIS_AUTHOR_NAME,
GitEmptyState::GENESIS_AUTHOR_EMAIL,
GitEmptyState::GENESIS_DATE_UNIX,
GitEmptyState::GENESIS_MESSAGE
)
}
}
fn main() {
println!("--- BIP-64MOD + GCC Genesis State ---");
println!("Commit Hash: {}", GitEmptyState::GENESIS_COMMIT_SHA256);
println!("Author: {} <{}>", GitEmptyState::GENESIS_AUTHOR_NAME, GitEmptyState::GENESIS_AUTHOR_EMAIL);
println!("Timestamp: {}", GitEmptyState::GENESIS_DATE_UNIX);
println!("NSEC: {}", GitEmptyState::GENESIS_NSEC);
let object_raw = builders::build_genesis_commit_object();
println!("\nRaw Git Commit Object:\n---\n{}---", object_raw);
}
#[cfg(feature = "nostr")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
get_file_hash_core::frost_mailbox_logic::simulate_frost_mailbox_post_signer()
}
#[cfg(not(feature = "nostr"))]
fn main() {
println!("This example requires the 'nostr' feature. Please run with: cargo run --example frost_mailbox_post --features nostr");
}
@Undisciplined ages wisdom: "[Review] Bitcoin Beans La Reserva Cacao". Time transforms #taste into magic with #foodstr & #drinkstr. Mature flavors https://stacker.news/items/1465134/r/Bitter
Great to hear. You're very talented. 👍
#[cfg(feature = "nostr")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
get_file_hash_core::frost_mailbox_logic::simulate_frost_mailbox_coordinator()
}
#[cfg(not(feature = "nostr"))]
fn main() {
println!("This example requires the 'nostr' feature. Please run with: cargo run --example frost_mailbox --features nostr");
}
.
Trump's Iran Speech PANICS The Market | PBD #768
https://www.youtube.com/live/53rigihjVZ8 via @PBDsPodcast
#[cfg(feature = "nostr")]
use frost_secp256k1_tr as frost; // MUST use the -tr variant for BIP-340/Nostr
#[cfg(feature = "nostr")]
use rand::thread_rng;
#[cfg(feature = "nostr")]
use serde_json::json;
#[cfg(feature = "nostr")]
use sha2::{Digest, Sha256};
#[cfg(feature = "nostr")]
use std::collections::BTreeMap;
#[cfg(feature = "nostr")]
use hex;
#[cfg(feature = "nostr")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut rng = thread_rng();
let (max_signers, min_signers) = (3, 2);
// 1. Setup Nostr Event Metadata
let pubkey_hex = "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"; // Example
let created_at = 1712050000;
let kind = 1;
let content = "Hello from ROAST threshold signatures!";
// 2. Serialize for Nostr ID (per NIP-01)
let event_json = json!([
0,
pubkey_hex,
created_at,
kind,
[],
content
]).to_string();
let mut hasher = Sha256::new();
hasher.update(event_json.as_bytes());
let event_id = hasher.finalize(); // This 32-byte hash is our signing message
// 3. FROST/ROAST Key Generation
let (shares, pubkey_package) = frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
&mut rng,
)?;
// 4. ROAST Coordination Simulation (Round 1: Commitments)
// In ROAST, the coordinator keeps a "session" open and collects commitments
let mut session_commitments = BTreeMap::new();
let mut signer_nonces = BTreeMap::new();
// Signers 1 and 3 respond first (Signer 2 is offline/slow)
for &id_val in &[1, 3] {
let id = frost::Identifier::try_from(id_val as u16)?;
let (nonces, comms) = frost::round1::commit(shares[&id].signing_share(), &mut rng);
session_commitments.insert(id, comms);
signer_nonces.insert(id, nonces);
}
// 5. Round 2: Signing the Nostr ID
let signing_package = frost::SigningPackage::new(session_commitments, &event_id);
let mut signature_shares = BTreeMap::new();
for (id, nonces) in signer_nonces {
let key_package: frost::keys::KeyPackage = shares[&id].clone().try_into()?;
let share = frost::round2::sign(&signing_package, &nonces, &key_package)?;
signature_shares.insert(id, share);
}
// 6. Aggregate into a BIP-340 Signature
let group_signature = frost::aggregate(
&signing_package,
&signature_shares,
&pubkey_package,
)?;
// 7. Verification (using BIP-340 logic)
pubkey_package.verifying_key().verify(&event_id, &group_signature)?;
println!("Nostr Event ID: {}", hex::encode(event_id));
println!("Threshold Signature (BIP-340): {}", hex::encode(group_signature.serialize()?));
println!("Successfully signed Nostr event using ROAST/FROST!");
Ok(())
}
#[cfg(not(feature = "nostr"))]
fn main() {
println!("This example requires the 'nostr' feature. Please run with: cargo run --example frost_bip_340 --features nostr");
}
#[cfg(feature = "nostr")]
use rand_chacha::ChaCha20Rng;
#[cfg(feature = "nostr")]
use rand_chacha::rand_core::SeedableRng;
#[cfg(feature = "nostr")]
use hex;
#[cfg(feature = "nostr")]
use frost_secp256k1_tr as frost;
#[cfg(feature = "nostr")]
use frost::keys::IdentifierList;
#[cfg(feature = "nostr")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Create a deterministic seed (e.g., 32 bytes of zeros or a Git Hash)
let seed_hex = "473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813";
let seed_bytes = hex::decode(seed_hex)?;
let mut rng = ChaCha20Rng::from_seed(seed_bytes.try_into().map_err(|_| "Invalid seed length")?);
let max_signers = 3;
let min_signers = 2;
////////////////////////////////////////////////////////////////////////////
// Round 0: Key Generation (Trusted Dealer)
////////////////////////////////////////////////////////////////////////////
// Using IdentifierList::Default creates identifiers 1, 2, 3...
let (shares, pubkey_package) = frost::keys::generate_with_dealer(
max_signers,
min_signers,
IdentifierList::Default,
&mut rng,
)?;
println!("--- Deterministic FROST Dealer ---");
println!("Threshold: {} of {}", min_signers, max_signers);
println!("Number of shares generated: {}", shares.len());
println!("\n--- Verifying Shares Against Commitments ---");
for (identifier, share) in &shares {
// The Deterministic Values (Scalar Hex)
// Because your seed is fixed to the EMPTY_BLOB_SHA256,
// the "redacted" values in your output are always the same.
// Here are the Secret Signing Shares (the private scalars) for your 2-of-3 setup:
//
// Participant,Identifier (x),Signing Share (f(x)) in Hex
// Participant 1,...0001,757f49553754988450d995c65a0459a0f5a703d7c585f95f468202d09a365f57
// Participant 2,...0002,a3c4835e32308cb11b43968962290bc9171f1f1ca90c21741890e4f326f9879b
// Participant 3,...0003,d209bd672d0c80dd65ad974c6a4dc1f138973a618c924988eaaa0715b3bcafdf
//
// println!("Participant Identifier: {:?} {:?}", identifier, _share);
//
// In FROST, the 'verify' method checks the share against the VSS commitment
match share.verify() {
Ok(_) => {
println!("Participant {:?}: Valid ✅", identifier);
}
Err(e) => {
println!("Participant {:?}: INVALID! ❌ Error: {:?}", identifier, e);
}
}
}
let pubkey_bytes = pubkey_package.verifying_key().serialize()?;
println!("Group Public Key (Hex Compressed): {}", hex::encode(&pubkey_bytes));
let x_only_hex = hex::encode(&pubkey_bytes[1..]);
println!("Group Public Key (Hex X-Only): {}", x_only_hex);
Ok(())
}
#[cfg(not(feature = "nostr"))]
fn main() {
println!("Run with --features nostr to enable this example.");
}
[workspace]
members = ["cargo:."]
# Config for 'dist'
[dist]
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.30.3"
# CI backends to support
ci = "github"
# The installers to generate for each app
installers = ["shell", "powershell", "homebrew", "msi"]
# A GitHub repo to push Homebrew formulas to
tap = "gnostr-org/homebrew-gnostr-org"
# Target platforms to build apps for (Rust target-triple syntax)
targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
# Path that installers should place binaries in
install-path = "CARGO_HOME"
# Publish jobs to run in CI
publish-jobs = ["homebrew"]
# Whether to install an updater program
install-updater = true
allow-dirty = ["ci"]