Skip to main content

OperatorSessionRecord

Struct OperatorSessionRecord 

Source
pub struct OperatorSessionRecord {
    pub sid: SessionId,
    pub token_digest: String,
    pub capability_manifest: Option<AgentProviderManifest>,
    pub joined_at_secs: u64,
    pub desc: Option<String>,
    pub observed: Vec<ObservedAssignment>,
    pub observed_total: u64,
}
Expand description

One persisted Operator login-flow session.

Field-for-field the durable subset of the server’s LoginSession — everything except the process-lifetime WS adapter state, which is rebuilt empty on reconnect.

§The bearer token is never stored

token_digest holds hex(SHA-256(bearer)) — the same fingerprint shape the /v1/sessions path already keys its store by, for the same reason (“the sid handed to the client is the token nonce itself (a bearer secret), so the server never uses it as a map key”; see mse_server::SessionStore). Every consumer of this record only ever compares a presented bearer (verify_bearer), so nothing downstream needs the plaintext — it exists only inside POST /v1/operators, between minting and the mint response.

Fields§

§sid: SessionId

Server-minted session id (S-<hex>).

§token_digest: String

hex(SHA-256(bearer)) of the auth token required on the WS upgrade and admin routes. Derive with Self::digest_of; compare with Self::verify_bearer. The plaintext bearer is deliberately absent (see the type doc).

§capability_manifest: Option<AgentProviderManifest>

Provider-owned effective capability manifest submitted at join.

§joined_at_secs: u64

Unix epoch seconds when POST /v1/operators minted this session.

§desc: Option<String>

The confirmed part of this session’s 記名 (model §4.2, D1): roughly 50 characters the joining AI wrote about what it is working on, fixed at join and never rewritten afterwards.

It is what the observed part cannot supply. Two drivers in the same worktree produce the same project_root / work_dir and can hold Runs of the same Blueprint; the sentence one of them wrote at join exists only in that conversation, which is what makes it an identifier (§4.2: 観測部分だけでは足りない).

None = the session joined without one. Kept as an absence rather than an empty string so a reader can tell “nothing was written” from “something was written and it was blank” — POST /v1/operators does not reject a missing desc (unlike A9 on the assignment side, D1-D5 name no 400), so the absence is a real and readable state.

D4: nothing matches on this. It is read by humans and AIs to tell sessions apart, never by the server to decide identity.

Additive with #[serde(default)] — rows persisted before the 記名 existed decode as None.

§observed: Vec<ObservedAssignment>

The observed part of this session’s 記名 (model §4.2, D2): one entry per seat this session was assigned, appended by the server at each Assign and never removed by any API.

Oldest first. Bounded by OBSERVED_CAP and de-duplicated per (run_id, slot) — see Self::record_observed.

Additive with #[serde(default)].

§observed_total: u64

How many Assigns have been recorded onto Self::observed over this session’s life, including the ones the ring has since dropped and the re-assignments folded into an existing entry.

Monotone. observed_total > observed.len() is the visible signal that the reader is looking at a window rather than the whole history.

Additive with #[serde(default)].

Implementations§

Source§

impl OperatorSessionRecord

Source

pub fn record_observed(&mut self, entry: ObservedAssignment)

Append one Assign to the observed part (D2).

Two shaping rules, both about keeping the log readable rather than about deleting anything:

  • One entry per (run_id, slot). Re-acquiring a seat this session already holds is the same fact with a newer timestamp, so the existing entry is replaced and moved to the newest position instead of accumulating a row per acquire. A driver that re-acquires after every reconnect would otherwise fill the whole window with one Run.
  • Newest OBSERVED_CAP kept. Past the cap the oldest entry is dropped.

Self::observed_total counts every call regardless, so a reader can tell that folding or dropping happened.

Source

pub fn last_activity_secs(&self) -> u64

When this session was last seen doing something — the newest ObservedAssignment::at_secs, or Self::joined_at_secs for a session that has never been assigned anything.

D5’s default ordering key. Derived rather than stored: a separate column would be a second thing to keep in step with the log, and the ring only ever drops entries older than the newest one, so the derivation cannot go stale.

Source

pub fn digest_of(bearer: &str) -> String

Digest a plaintext bearer into the at-rest shape (Self::token_digest).

Callers mint a bearer with operator_bearer_token and keep the plaintext only long enough to answer the mint request.

Source

pub fn verify_bearer(&self, bearer: &str) -> bool

Constant-time check of a presented bearer against Self::token_digest.

The comparison runs over the two digests (fixed-width hex), so it carries no timing signal about the bearer itself.

Trait Implementations§

Source§

impl Clone for OperatorSessionRecord

Source§

fn clone(&self) -> OperatorSessionRecord

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for OperatorSessionRecord

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for OperatorSessionRecord

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for OperatorSessionRecord

Source§

fn eq(&self, other: &OperatorSessionRecord) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for OperatorSessionRecord

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for OperatorSessionRecord

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> MaybeSend for T

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Serialize for T
where T: Serialize + ?Sized,

Source§

fn erased_serialize(&self, serializer: &mut dyn Serializer) -> Result<(), Error>

Source§

fn do_erased_serialize( &self, serializer: &mut dyn Serializer, ) -> Result<(), ErrorImpl>

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more