pub struct Server { /* private fields */ }Implementations§
Source§impl Server
impl Server
Sourcepub async fn start(
stores: Arc<ServerStores>,
config: ServeConfig,
) -> Result<Server>
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.
Sourcepub fn bus(&self) -> &Arc<EventBus> ⓘ
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.
Sourcepub fn host_key_fingerprint(&self) -> &str
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.
Sourcepub fn ssh_addr(&self) -> SocketAddr
pub fn ssh_addr(&self) -> SocketAddr
The address the SSH listener actually bound.
Sourcepub fn metrics_addr(&self) -> Option<SocketAddr>
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.
Sourcepub fn grpc_addr(&self) -> Option<SocketAddr>
pub fn grpc_addr(&self) -> Option<SocketAddr>
The address the gRPC listener actually bound, if it was started.
Sourcepub fn grpc_endpoint(&self) -> Option<(SocketAddr, bool)>
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.
Sourcepub fn bootstrap_window_open(&self) -> bool
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.
Sourcepub fn stores(&self) -> &Arc<ServerStores> ⓘ
pub fn stores(&self) -> &Arc<ServerStores> ⓘ
The stores behind both listeners.
Sourcepub async fn wait(self) -> Result<()>
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.
Sourcepub async fn shutdown(self) -> Result<()>
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.
Sourcepub fn admission(&self) -> AdmissionStats
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.
Sourcepub fn metrics(&self) -> MetricsSnapshot
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.
Sourcepub fn metrics_handle(&self) -> &Arc<ServerMetrics> ⓘ
pub fn metrics_handle(&self) -> &Arc<ServerMetrics> ⓘ
The counter block itself, for a caller that wants to keep a handle to it.
Sourcepub fn log_metrics(&self)
pub fn log_metrics(&self)
Write the counter line now, without waiting for the next heartbeat.
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for Server
impl !UnwindSafe for Server
impl Freeze for Server
impl Send for Server
impl Sync for Server
impl Unpin for Server
impl UnsafeUnpin for Server
Blanket Implementations§
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
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
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request