manifoldbt

0.2.0
License
Unknown license
Published
March 18, 2026
6h ago
Package Registry
README badge Customize →
License Sources
SourceLicenseClass
Licensie (detected)
Pending-
PyPI (reported)
Not reported-

License detection is still in progress for this version.

Loading dependencies…
License File
use base64::Engine as _;
use chrono::{DateTime, Utc};
use ed25519_dalek::{Signature, Verifier, VerifyingKey, PUBLIC_KEY_LENGTH};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};

const B64: base64::engine::general_purpose::GeneralPurpose =
    base64::engine::general_purpose::URL_SAFE_NO_PAD;

/// The public key used to verify license signatures.
/// Generated once with `bt-keygen generate-keys`, embedded at compile time.
/// Replace this with your actual public key bytes.
const PUBLIC_KEY_BYTES: [u8; PUBLIC_KEY_LENGTH] = [
    0xfe, 0x47, 0x60, 0x6b, 0x0b, 0x21, 0x27, 0xf5, 0xe9, 0x90, 0x9b, 0x7e, 0xbb, 0x5b, 0xa4, 0x62,
    0xe1, 0xa4, 0x26, 0x01, 0xb7, 0x71, 0xbf, 0xc4, 0x21, 0x3d, 0x0c, 0x09, 0x23, 0x6a, 0xab, 0x3d,
];

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum LicenseFeature {
    /// Full Pro license — all features
    Pro,
}

/// The license payload that gets signed.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LicensePayload {
    pub email: String,
    pub license_id: String,
    pub features: Vec<LicenseFeature>,
    pub max_devices: u32,
    pub updates_until: DateTime<Utc>,
    pub issued_at: DateTime<Utc>,
}

/// A complete license: payload + signature.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct License {
    pub payload: LicensePayload,
    pub signature: String,
}

impl License {
    /// Decode a license from a base64-encoded string.
    pub fn from_key(key: &str) -> Result<Self, crate::LicenseError> {
        let bytes = B64
            .decode(key.trim())
            .map_err(|_| crate::LicenseError::InvalidKey)?;

        let license: License =
            serde_json::from_slice(&bytes).map_err(|_| crate::LicenseError::InvalidKey)?;

        license.verify_signature()?;
        Ok(license)
    }

    /// Verify the Ed25519 signature against the embedded public key.
    fn verify_signature(&self) -> Result<(), crate::LicenseError> {
        let verifying_key = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES)
            .map_err(|_| crate::LicenseError::InvalidKey)?;

        let payload_json =
            serde_json::to_string(&self.payload).map_err(|_| crate::LicenseError::InvalidKey)?;

        let sig_bytes = B64
            .decode(&self.signature)
            .map_err(|_| crate::LicenseError::InvalidKey)?;

        let signature =
            Signature::from_slice(&sig_bytes).map_err(|_| crate::LicenseError::InvalidKey)?;

        verifying_key
            .verify(payload_json.as_bytes(), &signature)
            .map_err(|_| crate::LicenseError::InvalidKey)?;

        Ok(())
    }

    /// Check if updates are still covered.
    pub fn updates_valid(&self) -> bool {
        Utc::now() < self.payload.updates_until
    }

    /// Check if this license grants a specific feature.
    pub fn has_feature(&self, feature: &LicenseFeature) -> bool {
        self.payload.features.contains(feature)
    }

    /// Get a stable hash of the license ID (for telemetry, no PII).
    pub fn id_hash(&self) -> String {
        let mut hasher = Sha256::new();
        hasher.update(self.payload.license_id.as_bytes());
        let result = hasher.finalize();
        hex::encode(&result[..8])
    }
}

/// List of Pro-only features for gating.
pub const PRO_FEATURES: &[&str] = &[
    "resolution_1m",
    "resolution_5m",
    "resolution_15m",
    "resolution_30m",
    "resolution_1h",
    "monte_carlo_unlimited",
    "walk_forward",
    "bayesian_optimization",
    "data_connectors",
    "tearsheets_export",
];

/// Check if a feature name requires Pro.
pub fn requires_pro(feature_name: &str) -> bool {
    PRO_FEATURES.contains(&feature_name)
}
Versions
2 versions
VersionLicensePublishedStatus
0.2.0 Latest Viewing-Mar 18, 2026 Pending
0.1.0 -Mar 18, 2026 Pending