pub struct ProxyAuth { /* private fields */ }Expand description
Mints, caches, and refreshes the proxy token for one MicroVM and one port.
Shared behind an Arc and mutated through &self, because every request needs
the headers and a request path that needed &mut would serialize the whole
session behind one lock.
Implementations§
Source§impl ProxyAuth
impl ProxyAuth
Sourcepub fn with_clock(
minter: Arc<dyn TokenMinter>,
port: u16,
clock: Arc<dyn Clock>,
) -> Self
pub fn with_clock( minter: Arc<dyn TokenMinter>, port: u16, clock: Arc<dyn Clock>, ) -> Self
A ProxyAuth with the default refresh interval, on clock.
Infallible where Self::with_refresh_after isn’t: DEFAULT_REFRESH_AFTER is a
constant this module owns, and a test below asserts it satisfies that guard.
ProxyAuth::new(minter, port) is this on tokio’s clock, in microvms_core::prelude.
Sourcepub fn with_refresh_after(
minter: Arc<dyn TokenMinter>,
port: u16,
refresh_after: Duration,
clock: Arc<dyn Clock>,
) -> Result<Self, Error>
pub fn with_refresh_after( minter: Arc<dyn TokenMinter>, port: u16, refresh_after: Duration, clock: Arc<dyn Clock>, ) -> Result<Self, Error>
A ProxyAuth with an explicit refresh interval and clock.
Refuses an interval at or above MAX_TOKEN_LIFETIME (S2, TRAP-9). An
interval at the ceiling is the specific mistake: it looks like “refresh when
the token expires”, and it means every refresh races the expiry it exists to
avoid. Refused locally rather than documented, because the symptom is a 401
from the proxy that reads as a wrong agent token.
Sourcepub fn port(&self) -> u16
pub fn port(&self) -> u16
The port this token is scoped to, and the PROXY_PORT_HEADER value.
Sourcepub fn mint_count(&self) -> u64
pub fn mint_count(&self) -> u64
How many tokens have been minted over this instance’s life.
Public because it is the only externally visible evidence that minting happens in the request path at all: a token cached forever and a token refreshed on schedule produce identical successful requests, and the count is what distinguishes them. STATE-8’s resume test asserts on it.
Sourcepub fn invalidate(&self)
pub fn invalidate(&self)
Drops the cached token so the next request mints a fresh one (STATE-8).
Called after a resume. The measured behaviour is that a resumed VM keeps its endpoint URL and its bootstrap state, but a token minted against the pre-suspend instance is not guaranteed to survive — and a stale-token rejection there reads exactly like a daemon that died.
Synchronous, so a state machine can call it from a non-async transition.
Sourcepub fn is_cached(&self) -> bool
pub fn is_cached(&self) -> bool
Whether a token is cached and still inside the refresh window.
Exposed for the resume lane: after Self::invalidate this is false, which is
a fact a test can assert without issuing a request.
Asked about this session’s own port, which is the one every request on the hot path
targets. A token warmed only for some other port would read false here, and that is
the right answer to “is a request ready to go” — see Self::fresh_token_for.
Sourcepub async fn headers(&self) -> Result<Vec<(String, String)>, Error>
pub async fn headers(&self) -> Result<Vec<(String, String)>, Error>
Every header this request needs, minting first if the cache is stale.
Both headers, always. The auth value is read out of the token’s map by name; the rest of the map rides along in case the platform adds a third header; and the port header is filled from this instance’s port unless the token already named one.
Sourcepub async fn headers_for_port(
&self,
port: u16,
) -> Result<Vec<(String, String)>, Error>
pub async fn headers_for_port( &self, port: u16, ) -> Result<Vec<(String, String)>, Error>
Both required headers, for a port this instance was not built with.
What a caller opening its own connection to some other port on the same VM needs —
the harness contract’s getPortEndpoint() -> {url, headers} is exactly this shape,
and reimplementing it outside this module would mean a second mint path (see the
module docs on why there is one).
The requested port wins over one the token map carries, which is the opposite of
Self::headers. The reason is that a caller naming a port is answering the
question the header asks, and quietly substituting a different port would send a
request the caller can neither predict nor debug: the rejection names the token.
Self::headers defers to the map instead because nobody named a port there.
The auth value is a bearer credential. Nothing in this crate logs the returned pairs and no type here stores them; a caller that logs them is past what this module can prevent.
Sourcepub async fn subprotocols(&self, port: u16) -> Result<[String; 3], Error>
pub async fn subprotocols(&self, port: u16) -> Result<[String; 3], Error>
The three subprotocols a WebSocket handshake through the endpoint proxy offers.
In wire order: WS_SUBPROTOCOL, the JWE behind
WS_AUTH_SUBPROTOCOL_PREFIX, and port behind WS_PORT_SUBPROTOCOL_PREFIX.
See the module docs for why a handshake carries these instead of the two headers,
and note that the platform strips all three before forwarding — so a server inside
the VM never negotiates them.
Minted through the same cache and refresh schedule as Self::headers: a
long-lived connection opened at minute fifty gets a token minted at minute fifty,
not the one from construction, and nothing here can mint a token this instance’s
schedule does not know about.
The middle string contains the credential. Same rule as
Self::headers_for_port: this crate stores and logs none of it.