pub struct TokenContract {
pub token_id: String,
pub issued_at_unix_secs: u64,
pub ttl: Duration,
pub nonce: String,
pub vendor_family: VendorId,
pub challenge_class: ChallengeClass,
pub single_use: bool,
pub bound_session: Option<String>,
pub description: String,
}Expand description
Lifecycle contract for a single challenge token.
The contract is the wire-level schema for “what the
scraper is allowed to do with this token”. It is a pure
data structure: validation logic lives in
TokenValidator,
nonce bookkeeping lives in
NonceBook.
§Example
use std::time::Duration;
use stygian_charon::token_lifecycle::{ChallengeClass, TokenContract};
use stygian_charon::vendor_classifier::VendorId;
let contract = TokenContract {
token_id: "cf-bypass-xyz".to_string(),
issued_at_unix_secs: 1_700_000_000,
ttl: Duration::from_mins(30),
nonce: "n-001".to_string(),
vendor_family: VendorId::Cloudflare,
challenge_class: ChallengeClass::Interstitial,
single_use: true,
bound_session: Some("session-abc".to_string()),
description: "Cloudflare interstitial bypass token".to_string(),
};
assert_eq!(contract.vendor_family, VendorId::Cloudflare);
assert!(contract.single_use);Fields§
§token_id: StringStable token identifier (vendor-issued or scraper-derived). Used as a stable key in audit logs.
issued_at_unix_secs: u64Unix epoch seconds when the token was issued. The
validator uses this with the supplied now_unix_secs
to compute the token’s age.
ttl: DurationTime-to-live the token was issued with. The validator
clamps this against the per-vendor
TokenPolicy::max_ttl
ceiling before applying it.
nonce: StringPer-issuance nonce. The validator enforces that every submission carries the same nonce and that a single-use nonce cannot be submitted twice.
vendor_family: VendorIdVendor family the token was issued for. Used for both the per-vendor policy lookup and the diagnostic invalidation routing.
challenge_class: ChallengeClassChallenge class the token is bound to. Used for both the default per-class policy and the diagnostic invalidation routing.
single_use: booltrue when the token may only be submitted once. The
validator marks the nonce as consumed on first
successful validation.
bound_session: Option<String>Optional sticky-session identifier the token is bound
to. When Some, the validator rejects submissions
whose session_id does not match.
description: StringShort human-readable description (operator log / audit).
Implementations§
Source§impl TokenContract
impl TokenContract
Sourcepub const fn age_secs(&self, now_unix_secs: u64) -> u64
pub const fn age_secs(&self, now_unix_secs: u64) -> u64
Effective age in seconds at the supplied now_unix_secs.
Returns 0 when now < issued_at_unix_secs (clock skew
or test fixtures where the supplied clock is before the
issuance timestamp). The validator still rejects the
token when the TTL check fires — clock skew is a
different invalidation path that the policy planner
surfaces via
InvalidationKind::Expired.
§Example
use std::time::Duration;
use stygian_charon::token_lifecycle::{ChallengeClass, TokenContract};
use stygian_charon::vendor_classifier::VendorId;
let contract = TokenContract {
token_id: "x".to_string(),
issued_at_unix_secs: 100,
ttl: Duration::from_mins(5),
nonce: "n".to_string(),
vendor_family: VendorId::Unknown,
challenge_class: ChallengeClass::None,
single_use: false,
bound_session: None,
description: String::new(),
};
assert_eq!(contract.age_secs(160), 60);
assert_eq!(contract.age_secs(50), 0);Sourcepub const fn is_expired(&self, now_unix_secs: u64) -> bool
pub const fn is_expired(&self, now_unix_secs: u64) -> bool
true when the token’s age at now_unix_secs exceeds
ttl.
This is the raw TTL check — the validator applies
the per-vendor max_ttl clamp before calling this
helper. Callers that want to validate on their own
(e.g. doctests) should respect the policy table the same
way the validator does.
§Example
use std::time::Duration;
use stygian_charon::token_lifecycle::{ChallengeClass, TokenContract};
use stygian_charon::vendor_classifier::VendorId;
let contract = TokenContract {
token_id: "x".to_string(),
issued_at_unix_secs: 0,
ttl: Duration::from_mins(1),
nonce: "n".to_string(),
vendor_family: VendorId::Unknown,
challenge_class: ChallengeClass::None,
single_use: false,
bound_session: None,
description: String::new(),
};
assert!(!contract.is_expired(30));
assert!(contract.is_expired(120));Trait Implementations§
Source§impl Clone for TokenContract
impl Clone for TokenContract
Source§fn clone(&self) -> TokenContract
fn clone(&self) -> TokenContract
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for TokenContract
impl Debug for TokenContract
Source§impl<'de> Deserialize<'de> for TokenContract
impl<'de> Deserialize<'de> for TokenContract
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for TokenContract
impl PartialEq for TokenContract
Source§impl Serialize for TokenContract
impl Serialize for TokenContract
impl Eq for TokenContract
impl StructuralPartialEq for TokenContract
Auto Trait Implementations§
impl Freeze for TokenContract
impl RefUnwindSafe for TokenContract
impl Send for TokenContract
impl Sync for TokenContract
impl Unpin for TokenContract
impl UnsafeUnpin for TokenContract
impl UnwindSafe for TokenContract
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.