Skip to main content

Server

Struct Server 

Source
pub struct Server { /* private fields */ }

Implementations§

Source§

impl Server

Source

pub async fn start( stores: Arc<ServerStores>, config: ServeConfig, ) -> Result<Server>

Bind everything and start accepting.

Returns once both sockets are bound, so a caller knows the real addresses before the first client connects, which is what makes port 0 usable in a test.

Source

pub fn bus(&self) -> &Arc<EventBus> ⓘ

The event bus.

The same object Events.Watch streams from, exposed here so an in-process caller (the replicator, and the tests that assert what it publishes) can subscribe without a gRPC listener and a credential in between.

Source

pub fn host_key_fingerprint(&self) -> &str

The SSH host key this server serves, SHA256:… — the fingerprint a client pins, read off the key that is actually loaded rather than off a file somebody remembers.

Source

pub fn ssh_addr(&self) -> SocketAddr

The address the SSH listener actually bound.

Source

pub fn metrics_addr(&self) -> Option<SocketAddr>

The address the scrape endpoint bound, if it was started.

None is the ordinary answer: nothing binds unless --metrics-bind asked for it. A test that asked for port 0 learns its port here.

Source

pub fn grpc_addr(&self) -> Option<SocketAddr>

The address the gRPC listener actually bound, if it was started.

Source

pub fn grpc_endpoint(&self) -> Option<(SocketAddr, bool)>

The bound gRPC address together with whether that listener authenticates, which is what serve prints.

One accessor rather than two, because the banner used to carry a hardcoded UNAUTHENTICATED in v1 beside a control plane that authenticates. A caller that cannot get the address without the verdict cannot print a stale one.

Source

pub fn bootstrap_window_open(&self) -> bool

Was the unauthenticated enrolment window open when this server started?

What serve prints beside the gRPC address. false is the ordinary answer, which is why the banner says nothing at all in that case.

Source

pub fn stores(&self) -> &Arc<ServerStores> ⓘ

The stores behind both listeners.

Source

pub async fn wait(self) -> Result<()>

Serve until a stop is requested: a signal, or on the appliance the ACPI power button (appliance_pid1::runtime::stop_request).

Not ctrl_c(). That is SIGINT alone, and nothing in an appliance’s life sends SIGINT: podman stop sends SIGTERM and every service manager sends SIGTERM. A cloud’s soft stop (UpCloud stop, QEMU system_powerdown) is not a signal at all: it is an ACPI power-button press the kernel reports on the acpi_event generic-netlink group (corrected 2026-09-28; this said it became SIGTERM, and a soft stop then waited out the cloud’s timeout and was hard-stopped). PID 1 also gets no default disposition for a signal it has not handled — the kernel discards it. See edda’s appliance_pid1::net.

Source

pub async fn shutdown(self) -> Result<()>

Ask both listeners to stop, and wait for them.

§Why the gRPC wait is bounded (measured 2026-08-03)

serve_with_incoming_shutdown is graceful: it stops accepting and then waits for every in-flight request to finish. Events.Watch (the event stream) is server-streaming and by design never finishes; it ends when the subscriber goes away. So one connected UI made this wait forever, and gunnar serve could not be stopped with Ctrl-C at all. Measured: a test binary wedged for eleven minutes with the watch connection still ESTAB, no CPU, no progress.

Graceful is still tried first — an in-flight Repos.Create should be allowed to land — but after GRPC_SHUTDOWN_GRACE the task is aborted and the process is free to exit. A watcher losing its stream at shutdown is what a watcher is built to handle. A server that cannot be stopped is not.

Source

pub fn admission(&self) -> AdmissionStats

What the SSH admission gate has done since start-up.

Here so an operator surface (and tests/) can read the saturation counters without reaching into gunnar-ssh.

Source

pub fn metrics(&self) -> MetricsSnapshot

Every counter this server keeps, read now.

One reading of one set: the SSH listener and the git transport write to the same block, so connections_refused and pushes_rejected are facts about the same server rather than two crates’ private tallies.

Source

pub fn metrics_handle(&self) -> &Arc<ServerMetrics> ⓘ

The counter block itself, for a caller that wants to keep a handle to it.

Source

pub fn log_metrics(&self)

Write the counter line now, without waiting for the next heartbeat.

Trait Implementations§

Source§

impl Drop for Server

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

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<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

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> 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> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<L> LayerExt<L> for L

Source§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in Layered.
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, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

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