All checks were successful
AITBC CLI Level 1 Commands Test / test-cli-level1 (push) Successful in 16s
api-endpoint-tests / test-api-endpoints (push) Successful in 35s
integration-tests / test-service-integration (push) Successful in 1m25s
package-tests / test-python-packages (map[name:aitbc-agent-sdk path:packages/py/aitbc-agent-sdk python_version:3.13]) (push) Successful in 16s
package-tests / test-python-packages (map[name:aitbc-cli path:. python_version:3.13]) (push) Successful in 14s
package-tests / test-python-packages (map[name:aitbc-core path:packages/py/aitbc-core python_version:3.13]) (push) Successful in 13s
package-tests / test-python-packages (map[name:aitbc-crypto path:packages/py/aitbc-crypto python_version:3.13]) (push) Successful in 10s
package-tests / test-python-packages (map[name:aitbc-sdk path:packages/py/aitbc-sdk python_version:3.13]) (push) Successful in 12s
package-tests / test-javascript-packages (map[name:aitbc-sdk node_version:24 path:packages/js/aitbc-sdk]) (push) Successful in 18s
python-tests / test-specific (push) Has been skipped
security-scanning / audit (push) Successful in 14s
systemd-sync / sync-systemd (push) Successful in 4s
package-tests / cross-language-compatibility (push) Successful in 2s
package-tests / package-integration-tests (push) Successful in 3s
Documentation Validation / validate-docs (push) Successful in 6m13s
python-tests / test (push) Successful in 14s
## 🚀 Central Virtual Environment Implementation - Created central venv at /opt/aitbc/venv for all services - Updated 34+ systemd services to use central python interpreter - Fixed PYTHONPATH configurations for proper module imports - Created aitbc-env wrapper script for environment management ## 📦 Requirements Management Overhaul - Consolidated 8 separate requirements.txt files into central requirements.txt - Added web3>=6.11.0 for blockchain functionality - Created automated requirements migrator tool (scripts/requirements_migrator.py) - Established modular requirements structure (requirements-modules/) - Generated comprehensive migration reports and documentation ## 🔧 Service Configuration Fixes - Fixed Adaptive Learning service domain imports (AgentStatus) - Resolved logging conflicts in zk_proofs and adaptive_learning_health - Created missing data modules (consumer_gpu_profiles.py) - Updated CLI to version 0.2.2 with proper import handling - Fixed infinite loop in CLI alias configuration ## 📡 Port Mapping and Service Updates - Updated blockchain node port from 8545 to 8005 - Added Adaptive Learning service on port 8010 - Consolidated P2P/sync into blockchain-node service - All 5 core services now operational and responding ## 📚 Documentation Enhancements - Updated SYSTEMD_SERVICES.md for Debian root usage (no sudo) - Added comprehensive VIRTUAL_ENVIRONMENT.md guide - Created REQUIREMENTS_MERGE_SUMMARY.md with migration details - Updated RUNTIME_DIRECTORIES.md for standard Linux paths - Fixed service port mappings and dependencies ## 🛠️ CLI Improvements - Fixed import errors and version display (0.2.2) - Resolved infinite loop in bashrc alias - Added proper error handling for missing command modules - Created aitbc-cli wrapper for clean execution ## ✅ Operational Status - 5/5 AITBC services running successfully - All health checks passing - Central virtual environment fully functional - Requirements management streamlined - Documentation accurate and up-to-date ## 🎯 Technical Achievements - Eliminated 7 redundant requirements.txt files - Reduced service startup failures from 34+ to 0 - Established modular dependency management - Created reusable migration tooling - Standardized Debian root deployment practices This represents a complete infrastructure modernization with improved reliability, maintainability, and operational efficiency.
114 lines
4.0 KiB
Python
114 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Keystore management for AITBC production keys.
|
|
Generates a random private key and encrypts it with a password using Fernet (AES-128).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import base64
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import secrets
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
from cryptography.fernet import Fernet
|
|
|
|
|
|
def derive_key(password: str, salt: bytes = b"") -> bytes:
|
|
"""Derive a 32-byte key from the password using SHA-256."""
|
|
if not salt:
|
|
salt = secrets.token_bytes(16)
|
|
# Simple KDF: hash(password + salt)
|
|
dk = hashlib.sha256(password.encode() + salt).digest()
|
|
return base64.urlsafe_b64encode(dk), salt
|
|
|
|
|
|
def encrypt_private_key(private_key_hex: str, password: str) -> dict:
|
|
"""Encrypt a hex-encoded private key with Fernet, returning a keystore dict."""
|
|
key, salt = derive_key(password)
|
|
f = Fernet(key)
|
|
token = f.encrypt(private_key_hex.encode())
|
|
return {
|
|
"cipher": "fernet",
|
|
"cipherparams": {"salt": base64.b64encode(salt).decode()},
|
|
"ciphertext": base64.b64encode(token).decode(),
|
|
"kdf": "sha256",
|
|
"kdfparams": {"dklen": 32, "salt": base64.b64encode(salt).decode()},
|
|
}
|
|
|
|
|
|
def create_keystore(address: str, password: str, keystore_dir: Path | str = "/var/lib/aitbc/keystore", force: bool = False) -> Path:
|
|
"""Create encrypted keystore file and return its path."""
|
|
keystore_dir = Path(keystore_dir)
|
|
keystore_dir.mkdir(parents=True, exist_ok=True)
|
|
out_file = keystore_dir / f"{address}.json"
|
|
|
|
if out_file.exists() and not force:
|
|
raise FileExistsError(f"Keystore file {out_file} exists. Use force=True to overwrite.")
|
|
|
|
private_key = secrets.token_hex(32)
|
|
encrypted = encrypt_private_key(private_key, password)
|
|
keystore = {
|
|
"address": address,
|
|
"crypto": encrypted,
|
|
"created_at": datetime.utcnow().isoformat() + "Z",
|
|
}
|
|
|
|
out_file.write_text(json.dumps(keystore, indent=2))
|
|
os.chmod(out_file, 0o600)
|
|
return out_file
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description="Generate encrypted keystore for an account")
|
|
parser.add_argument("address", help="Account address (e.g., aitbc1treasury)")
|
|
parser.add_argument("--output-dir", type=Path, default=Path("/var/lib/aitbc/keystore"), help="Keystore directory")
|
|
parser.add_argument("--force", action="store_true", help="Overwrite existing keystore file")
|
|
parser.add_argument("--password", help="Encryption password (or read from KEYSTORE_PASSWORD / keystore/.password)")
|
|
args = parser.parse_args()
|
|
|
|
out_dir = args.output_dir
|
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
out_file = out_dir / f"{args.address}.json"
|
|
|
|
if out_file.exists() and not args.force:
|
|
print(f"Keystore file {out_file} exists. Use --force to overwrite.")
|
|
return
|
|
|
|
# Determine password: CLI > env var > password file
|
|
password = args.password
|
|
if not password:
|
|
password = os.getenv("KEYSTORE_PASSWORD")
|
|
if not password:
|
|
pw_file = Path("/var/lib/aitbc/keystore/.password")
|
|
if pw_file.exists():
|
|
password = pw_file.read_text().strip()
|
|
if not password:
|
|
print("No password provided. Set KEYSTORE_PASSWORD, pass --password, or create /var/lib/aitbc/keystore/.password")
|
|
sys.exit(1)
|
|
|
|
print(f"Generating keystore for {args.address}...")
|
|
private_key = secrets.token_hex(32)
|
|
print(f"Private key (hex): {private_key}")
|
|
print("** SAVE THIS KEY SECURELY ** (It cannot be recovered from the encrypted file without the password)")
|
|
|
|
encrypted = encrypt_private_key(private_key, password)
|
|
keystore = {
|
|
"address": args.address,
|
|
"crypto": encrypted,
|
|
"created_at": datetime.utcnow().isoformat() + "Z",
|
|
}
|
|
|
|
out_file.write_text(json.dumps(keystore, indent=2))
|
|
os.chmod(out_file, 0o600)
|
|
print(f"[+] Keystore written to {out_file}")
|
|
print(f"[!] Keep the password safe. Without it, the private key cannot be recovered.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|