+
ā GitHub Star Growth Tracker - {OWNER}
+
Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}
+
+
+
+
{total_stars:,}
+
Total Stars
+
+
+
{len(repos)}
+
Repositories
+
+
+
{sum(1 for d in deltas if d['daily_delta'] > 0)}
+
Growing Today
+
+
+
+
+
Top Growers (Last {days} days)
+
+
+
+
All Repositories
+
+
+
+ | Repository |
+ Stars |
+ Daily Ī |
+
+
+
+"""
+
+ for d in deltas:
+ delta_class = "positive" if d["daily_delta"] > 0 else "negative" if d["daily_delta"] < 0 else ""
+ delta_sign = "+" if d["daily_delta"] > 0 else ""
+ html += f"""
+
+ | {d['repo']} |
+ {d['stars']:,} |
+ {delta_sign}{d['daily_delta']} |
+
+"""
+
+ html += """
+
+
+
+
+
+
+"""
+ return html
+
+
+def print_terminal(repos: List[RepoStars], deltas: List[Dict[str, Any]]) -> None:
+ """Print terminal dashboard."""
+ total_stars = get_total_stars(repos)
+
+ print(f"GitHub Star Growth Tracker - {OWNER}")
+ print("=" * 60)
+ print(f"Total Stars: {total_stars:,}")
+ print(f"Repositories: {len(repos)}")
+ print()
+ print("Top Growers Today:")
+ print("-" * 40)
+
+ for d in deltas[:10]:
+ delta_sign = "+" if d["daily_delta"] > 0 else ""
+ print(f" {d['repo']:<30} {d['stars']:>5} {delta_sign}{d['daily_delta']}")
+
+ print()
+ print("All Repositories:")
+ print("-" * 40)
+ for d in deltas:
+ delta_sign = "+" if d["daily_delta"] > 0 else ""
+ print(f" {d['repo']:<30} {d['stars']:>5} {delta_sign}{d['daily_delta']}")
+
+
+def main():
+ parser = argparse.ArgumentParser(description="GitHub Star Growth Tracker")
+ parser.add_argument("--fetch", action="store_true", help="Fetch latest star data")
+ parser.add_argument("--html", action="store_true", help="Generate HTML dashboard")
+ parser.add_argument("--days", type=int, default=30, help="Days of history to show")
+ parser.add_argument("--db", type=str, default=str(DB_PATH), help="Database path")
+ args = parser.parse_args()
+
+ db_path = Path(args.db)
+ ensure_db(db_path)
+
+ if args.fetch:
+ print("Fetching repositories...")
+ repos = get_all_repos(OWNER)
+ print(f"Found {len(repos)} repositories with {get_total_stars(repos):,} total stars")
+
+ print("Saving snapshot...")
+ save_snapshot(db_path, repos)
+ print("Done!")
+
+ if args.html:
+ repos = get_all_repos(OWNER)
+ save_snapshot(db_path, repos)
+
+ html = generate_html(db_path, repos, args.days)
+ output_path = Path("star_tracker.html")
+ output_path.write_text(html)
+ print(f"HTML dashboard saved to {output_path}")
+
+ # Default: show terminal output
+ repos = get_all_repos(OWNER)
+ save_snapshot(db_path, repos)
+ deltas = calculate_daily_deltas(db_path)
+ print_terminal(repos, deltas)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/health_check_cli/README.md b/tools/health_check_cli/README.md
new file mode 100644
index 000000000..763fce6c7
--- /dev/null
+++ b/tools/health_check_cli/README.md
@@ -0,0 +1,56 @@
+# RustChain Health Check CLI
+
+A CLI tool that queries all 3 RustChain attestation nodes and displays their health status in a formatted table.
+
+## Bounty
+
+This tool was created for [Bounty #1111](https://github.com/Scottcjn/rustchain-bounties/issues/1111) - 8 RTC Reward.
+
+## Features
+
+- Queries all 3 attestation nodes:
+ - 50.28.86.131:443
+ - 50.28.86.153:443
+ - 76.8.228.245:8099
+- Displays: version, uptime, db_rw status, tip age
+- Formatted table output
+- JSON output option
+- Exit code reflects node availability
+
+## Usage
+
+```bash
+# Default table output
+python3 tools/health_check_cli/rustchain_health_check.py
+
+# JSON output
+python3 tools/health_check_cli/rustchain_health_check.py --json
+
+# Verbose mode (show errors)
+python3 tools/health_check_cli/rustchain_health_check.py -v
+```
+
+## Example Output
+
+```
+RustChain Node Health Check
+Timestamp: 2026-03-07T23:00:00Z
+
+Host Port Status Version Uptime DB RW Tip Age
+-----------------------------------------------------------------------------------------
+50.28.86.131 443 ONLINE 1.2.3 5d 12h 30m true 30s
+50.28.86.153 443 ONLINE 1.2.3 3d 8h 15m true 45s
+76.8.228.245 8099 ONLINE 1.2.3 2d 4h 50m true 1m
+
+Summary: 3/3 nodes online
+```
+
+## Requirements
+
+- Python 3.7+
+- Stdlib only (no external dependencies)
+
+## Exit Codes
+
+- `0` - All nodes online
+- `1` - One or more nodes offline
diff --git a/tools/health_check_cli/rustchain_health_check.py b/tools/health_check_cli/rustchain_health_check.py
new file mode 100644
index 000000000..45ac85303
--- /dev/null
+++ b/tools/health_check_cli/rustchain_health_check.py
@@ -0,0 +1,181 @@
+#!/usr/bin/env python3
+"""
+RustChain Health Check CLI
+
+Queries all 3 attestation nodes and displays health status.
+Bounty: #1111 - 8 RTC Reward
+"""
+
+import argparse
+import json
+import ssl
+import sys
+import time
+import urllib.error
+import urllib.request
+from dataclasses import dataclass
+from typing import Any, Dict, List, Optional, Tuple
+
+
+ATTESTATION_NODES = [
+ ("50.28.86.131", 443, True),
+ ("50.28.86.153", 443, True),
+ ("76.8.228.245", 8099, False),
+]
+
+
+def utc_iso(ts: Optional[float] = None) -> str:
+ ts = time.time() if ts is None else ts
+ return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(ts))
+
+
+def _ssl_context(insecure: bool) -> Optional[ssl.SSLContext]:
+ if not insecure:
+ return None
+ ctx = ssl.create_default_context()
+ ctx.check_hostname = False
+ ctx.verify_mode = ssl.CERT_NONE
+ return ctx
+
+
+def http_json_get(url: str, timeout_s: int, insecure: bool) -> Tuple[bool, Any, str]:
+ try:
+ req = urllib.request.Request(url, headers={"User-Agent": "rustchain-health-check/1.0"})
+ with urllib.request.urlopen(req, timeout=timeout_s, context=_ssl_context(insecure)) as resp:
+ body = resp.read(1024 * 1024).decode("utf-8", errors="replace")
+ try:
+ return True, json.loads(body), ""
+ except Exception:
+ return False, None, "invalid_json"
+ except urllib.error.HTTPError as e:
+ return False, None, f"http_{e.code}"
+ except Exception as e:
+ return False, None, "unreachable"
+
+
+@dataclass
+class NodeHealth:
+ host: str
+ port: int
+ online: bool
+ version: str = "N/A"
+ uptime: str = "N/A"
+ db_rw: str = "N/A"
+ tip_age: str = "N/A"
+ error: str = ""
+
+
+def check_node(host: str, port: int, use_https: bool) -> NodeHealth:
+ protocol = "https" if use_https else "http"
+ url = f"{protocol}://{host}:{port}/health"
+
+ health = NodeHealth(host=host, port=port, online=False)
+
+ ok, data, err = http_json_get(url, timeout_s=10, insecure=True)
+
+ if not ok:
+ health.error = err
+ return health
+
+ health.online = True
+
+ # Parse version
+ if "version" in data:
+ health.version = str(data["version"])
+ elif "data" in data and "version" in data.get("data", {}):
+ health.version = str(data["data"]["version"])
+
+ # Parse uptime
+ if "uptime" in data:
+ health.uptime = str(data["uptime"])
+ elif "data" in data and "uptime" in data.get("data", {}):
+ health.uptime = str(data["data"]["uptime"])
+
+ # Parse db_rw status
+ if "db_rw" in data:
+ health.db_rw = str(data["db_rw"])
+ elif "data" in data and "db_rw" in data.get("data", {}):
+ health.db_rw = str(data["data"]["db_rw"])
+
+ # Parse tip age
+ if "tip_age" in data:
+ health.tip_age = str(data["tip_age"])
+ elif "data" in data and "tip_age" in data.get("data", {}):
+ health.tip_age = str(data["data"]["tip_age"])
+ elif "data" in data and "tip" in data.get("data", {}):
+ tip = data["data"]["tip"]
+ if isinstance(tip, dict) and "age" in tip:
+ health.tip_age = str(tip["age"])
+
+ return health
+
+
+def format_table(healths: List[NodeHealth]) -> str:
+ # Header
+ header = f"{'Host':<20} {'Port':<6} {'Status':<8} {'Version':<12} {'Uptime':<15} {'DB RW':<8} {'Tip Age':<15}"
+ separator = "-" * len(header)
+
+ lines = [header, separator]
+
+ for h in healths:
+ status = "ONLINE" if h.online else "OFFLINE"
+ lines.append(
+ f"{h.host:<20} {h.port:<6} {status:<8} {h.version:<12} {h.uptime:<15} {h.db_rw:<8} {h.tip_age:<15}"
+ )
+
+ return "\n".join(lines)
+
+
+def main():
+ parser = argparse.ArgumentParser(description="RustChain Health Check CLI")
+ parser.add_argument("--json", action="store_true", help="Output in JSON format")
+ parser.add_argument("-v", "--verbose", action="store_true", help="Show detailed errors")
+ args = parser.parse_args()
+
+ results: List[NodeHealth] = []
+
+ for host, port, use_https in ATTESTATION_NODES:
+ health = check_node(host, port, use_https)
+ results.append(health)
+
+ if args.json:
+ output = {
+ "timestamp": utc_iso(),
+ "nodes": []
+ }
+ for h in results:
+ node_data = {
+ "host": h.host,
+ "port": h.port,
+ "online": h.online,
+ "version": h.version,
+ "uptime": h.uptime,
+ "db_rw": h.db_rw,
+ "tip_age": h.tip_age,
+ }
+ if h.error:
+ node_data["error"] = h.error
+ output["nodes"].append(node_data)
+ print(json.dumps(output, indent=2))
+ else:
+ print("RustChain Node Health Check")
+ print(f"Timestamp: {utc_iso()}")
+ print()
+ print(format_table(results))
+
+ if args.verbose:
+ for h in results:
+ if h.error:
+ print(f"\nError for {h.host}:{h.port}: {h.error}")
+
+ # Summary
+ online_count = sum(1 for h in results if h.online)
+ print(f"\nSummary: {online_count}/{len(results)} nodes online")
+
+ # Exit code: 0 if all online, 1 if any offline
+ all_online = all(h.online for h in results)
+ sys.exit(0 if all_online else 1)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/rustchain-address-validator/.github/workflows/ci.yml b/tools/rustchain-address-validator/.github/workflows/ci.yml
new file mode 100644
index 000000000..2d93e015b
--- /dev/null
+++ b/tools/rustchain-address-validator/.github/workflows/ci.yml
@@ -0,0 +1,33 @@
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ test:
+ name: Test
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Rust
+ uses: dtolnay/rust-toolchain@stable
+
+ - name: Cache cargo
+ uses: Swatinem/rust-cache@v2
+
+ - name: Build
+ run: cargo build --release
+
+ - name: Run tests
+ run: cargo test
+
+ - name: Clippy
+ run: cargo clippy -- -D warnings
+
+ - name: Format check
+ run: cargo fmt --check
diff --git a/tools/rustchain-address-validator/.gitignore b/tools/rustchain-address-validator/.gitignore
new file mode 100644
index 000000000..6f43f02b3
--- /dev/null
+++ b/tools/rustchain-address-validator/.gitignore
@@ -0,0 +1,13 @@
+# Build artifacts
+target/
+Cargo.lock
+
+# IDE
+.idea/
+.vscode/
+*.swp
+*.swo
+
+# OS
+.DS_Store
+Thumbs.db
diff --git a/tools/rustchain-address-validator/Cargo.toml b/tools/rustchain-address-validator/Cargo.toml
new file mode 100644
index 000000000..8a8b58122
--- /dev/null
+++ b/tools/rustchain-address-validator/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "rustchain-address-validator"
+version = "0.1.0"
+edition = "2021"
+description = "RTC Address generator and validator for RustChain"
+license = "MIT"
+authors = ["sososonia-cyber"]
+repository = "https://github.com/sososonia-cyber/Rustchain"
+
+[dependencies]
+bs58 = "0.5"
+sha2 = "0.10"
+ed25519-dalek = { version = "2.1", features = ["rand_core"] }
+rand = "0.8"
+hex = "0.4"
+clap = { version = "4.5", features = ["derive"] }
+
+[dev-dependencies]
+
+[[bin]]
+name = "rtc-address"
+path = "src/main.rs"
+
+[lib]
+name = "rustchain_address"
+path = "src/lib.rs"
diff --git a/tools/rustchain-address-validator/README.md b/tools/rustchain-address-validator/README.md
new file mode 100644
index 000000000..bbc5ecb04
--- /dev/null
+++ b/tools/rustchain-address-validator/README.md
@@ -0,0 +1,84 @@
+# RustChain Address Validator & Generator
+
+A Rust CLI tool for generating and validating RTC addresses on the RustChain network.
+
+## Features
+
+- Generate new RTC addresses with private keys
+- Validate existing RTC addresses
+- Derive address from private key
+
+## Installation
+
+```bash
+cargo install --path .
+```
+
+Or build and run directly:
+
+```bash
+cargo build --release
+./target/release/rtc-address --help
+```
+
+## Usage
+
+### Generate a new address
+
+```bash
+rtc-address generate
+```
+
+Output:
+```
+=== Generated RTC Address ===
+
+Address: RTCxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+Private Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
+IMPORTANT: Save your private securely!
+ Anyone with your private key can access your funds.
+```
+
+### Validate an address
+
+```bash
+rtc-address validate RTCxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+```
+
+### Derive address from private key
+
+```bash
+rtc-address from-key