Skip to main content

ServeConfig

Struct ServeConfig 

Source
pub struct ServeConfig {
Show 32 fields pub ssh_bind: SocketAddr, pub grpc_bind: BindSpec, pub grpc_bind_device: Option<String>, pub admin_key: Option<String>, pub restore_pubkeys: Vec<String>, pub no_grpc: bool, pub wg_only: bool, pub twin_role: bool, pub allow_anonymous: bool, pub anonymous: AnonymousPolicy, pub runner: Option<WatchConfig>, pub grpc_tls: Option<GrpcTls>, pub web_relying_parties: Vec<String>, pub web_require_user_verification: bool, pub identity_root_site: bool, pub grpc_replication_bind: Option<SocketAddr>, pub max_concurrent_transfers: Option<usize>, pub memory_budget_bytes: Option<u64>, pub admission_wait: Option<Duration>, pub max_transfers_per_principal: Option<usize>, pub max_anonymous_transfers: Option<usize>, pub per_principal_limit_is_hard: bool, pub control_plane_workers: Option<usize>, pub max_object_bytes: Option<u64>, pub metrics_interval: Option<Duration>, pub metrics_bind: Option<SocketAddr>, pub replication_view: ReplicationView, pub tenants_view: TenantView, pub flow_view: FlowView, pub wire: WireConfig, pub disk_floor: DiskFloor, pub space_probe: Option<Arc<dyn SpaceProbe>>,
}
Expand description

What gunnar serve was asked for.

Fields§

§ssh_bind: SocketAddr

Where the SSH listener binds.

§grpc_bind: BindSpec

Where the gRPC control plane binds. Loopback by default.

A BindSpec rather than a SocketAddr because an appliance’s private address is DHCP-learned: the declaration names eth1:50051 and run resolves it, ONCE, after GUNNAR_NET has brought the link up and before anything binds. Downstream — the readiness line, the dashboard row, Admin.Health — then reports the address the listener actually took, not the spelling it was asked for.

§grpc_bind_device: Option<String>

The DEVICE the control plane listens on, when grpc_bind was spelled as an interface (eth1:50051) — set by run/[start] from that spelling and honoured as SO_BINDTODEVICE on the listener socket, so the interface name means the interface and not merely the address it held at boot. MEASURED 2026-09-05 (the KVM bench rig, two slirp NICs on the same 10.0.2.0/24): an address-only bind of eth1’s 10.0.2.15 answered through eth0, because both NICs held that address and the route decided. On a cloud whose utility NIC is eth1 on its own subnet the two agree; the device bind makes them agree everywhere. None for an address spec.

§admin_key: Option<String>

An OpenSSH public key line to install as the bootstrap admin when the identity table is empty (the secret-store seam).

§restore_pubkeys: Vec<String>

The keys Bootstrap.Restore is taken from (--restore-pubkey, h-v1-6a): OpenSSH public key lines or @<path>, resolved and checked at start. An installer-fed box adds the fed admin keys itself; a box with neither refuses every restore by name.

§no_grpc: bool

Start the SSH listener only.

§wg_only: bool

--wg-only: every box-to-box connection runs over the estate’s WireGuard overlay (owner, 2026-10-04). The address the control plane is bound on (eth1:50051, the utility network) serves NOTHING: the control plane and the replication listener answer only on the tunnel’s address (crate::tunnel_addr), and SSH — bound for the public’s git clients — drops a connection that arrives at the utility address before a byte is spoken. A plain-IP connection between two boxes is refused.

§twin_role: bool

--role twin: no forge listener from the first boot (loopback only), whether or not Bootstrap.BecomeTwin has run yet.

§allow_anonymous: bool

Whether a caller with no key may clone a repository marked Visibility::Public. true by default; nothing is anonymously readable until a repository is deliberately made public, because Private is the default for a new one.

Reaches SshConfig::allow_anonymous and HttpConfig::allow_anonymous, from this one field. Two fields is how one of them ends up stubbed: an operator who passed --no-anonymous and got it honoured on one transport has a server that serves strangers and reports that it does not. The gRPC control plane has no anonymous mode to switch, at any setting.

§anonymous: AnonymousPolicy

--anonymous allow|refuse (the browse replica): whether an anonymous read is SERVED here or sent to the browse replica the refusal names. Orthogonal to allow_anonymous, which is whether a stranger authenticates at all; this is what the dispatcher tells one who did. Both front doors read it through the one Control.

§runner: Option<WatchConfig>

Build every push, in this process, hermetically (roadmap phase 3).

None — the default — is a server that stores and serves and does not build. Some starts the runner on the same event bus every push publishes on, which is what makes the golden path’s fourth step nothing at all.

It is in the serving process on purpose: a second process cannot subscribe to an in-process bus, and a runner that polled the attestation log instead would build a push some time after it landed rather than because it landed.

§grpc_tls: Option<GrpcTls>

Require a client certificate on the gRPC control plane, minted by this server’s own issuer (the mTLS plan).

None — the default — is the loopback shape the unauthenticated v1 assumes: the listener binds 127.0.0.1 and a credential in metadata is the identity. Some(GrpcTls::SelfMinted) is the shape a control plane reachable off the box needs, and it changes two things at once: the transport refuses a peer whose certificate this issuer did not sign, and the gate maps the certificate that got through to an ACL principal.

Deliberately not on by default. Turning it on rewrites how every existing client must connect, and a security default that silently breaks the working configuration gets switched back off in a hurry — which leaves it off everywhere. It is a flag an operator turns on when the listener leaves loopback, and --grpc-bind off loopback without it is what the startup warning names.

§web_relying_parties: Vec<String>

--web-relying-party, unparsed: the WebAuthn relying parties the gRPC plane answers for (gunnar_grpc::webauthn::RelyingParty).

§web_require_user_verification: bool

--web-require-user-verification (A34-12).

§identity_root_site: bool

--identity-root-site (IDENTITY-HOST): sign-up is open here.

§grpc_replication_bind: Option<SocketAddr>

The mutual replication listener (--grpc-replication-bind, lane T9): the door a twin follows this box through. Served only with a client certificate this box’s issuer signed, TLS 1.3 only, its leaf minted from the issuer at every start under the fixed name gunnar_grpc::client_signer::REPLICATION_SERVER_NAME.

None DERIVES it, and the derivation is what lets the ONE medium carry no flag: a control plane bound off loopback (the appliance, 0.0.0.0:50051) gets this listener on the same address, port + 1 (50052); a loopback one (a laptop, a test) gets none. Its main listener stays exactly what it was, because turning --grpc-tls-self on for the whole plane would lock out every client that has no certificate — and the bootstrap door with them.

§max_concurrent_transfers: Option<usize>

How many git transfers may be in flight across the whole server. None takes DEFAULT_MAX_CONCURRENT_TRANSFERS.

The fix note on the admission gate says every one of these numbers needs to be settable, because the right one is deployment-specific; until now neither was reachable from gunnar serve and the two defaults were the only values any deployment could have. That is also why the pressure harness could not reach contention without 320 client processes.

§memory_budget_bytes: Option<u64>

The memory budget the admission ledger may give out to git operations in flight, in bytes (gate 2). None takes crate::budget::budgets’s derived figure — MemAvailable x crate::budget::TRANSFER_MEMORY_PERCENT — rather than gunnar-ssh’s compiled-in 8 GiB, which was a number about the cgroup one bakeoff ran in and not about this box.

§admission_wait: Option<Duration>

How long a transfer may wait for a slot before it is refused. None takes gunnar_ssh::governor::DEFAULT_ADMISSION_WAIT; Some(Duration::ZERO) restores the instant refusal.

§max_transfers_per_principal: Option<usize>

A principal’s guaranteed floor and its share of a contended server. None takes DEFAULT_MAX_TRANSFERS_PER_PRINCIPAL.

§max_anonymous_transfers: Option<usize>

How many git reads may be served without a key at once. None takes DEFAULT_MAX_ANONYMOUS_TRANSFERS.

Note the asymmetry with the field above, which is deliberate: that one is a floor a principal is guaranteed and may exceed on a quiet box, this one is a wall that binds however empty the server is. See gunnar_ssh::admission for why work-conserving is the right instinct for a tenant and the wrong one for a stranger.

§per_principal_limit_is_hard: bool

Make the per-principal number an unconditional wall again rather than a max-min share. See SshConfig::per_principal_limit_is_hard.

§control_plane_workers: Option<usize>

How many worker threads the control plane’s own runtime gets. None derives it from control_plane_workers.

A knob rather than a constant, because the right number is a deployment question: a box whose operators live in the UI wants more, an appliance serving one CI fleet wants fewer. A test also has to be able to make the pool small enough to saturate it deliberately.

§max_object_bytes: Option<u64>

The largest object Refs.ReadObject will inflate. None takes CoreControlPlane’s default of 64 MiB; Some(0) means no ceiling.

A knob for the same reason control_plane_workers is one: the number is memory budget / concurrent inflates, and the budget is a deployment question. It is also the only one of these that turns a request which works today into a refusal, which is exactly why it must be reachable rather than compiled in.

§metrics_interval: Option<Duration>

How often the counter heartbeat is written. None turns it off.

The heartbeat answers “what is this server doing right now” from a log the operator already has open, with nothing to scrape. It stays quiet while nothing changes, so an idle appliance does not fill its journal.

§metrics_bind: Option<SocketAddr>

Where the scrape endpoint binds. None does not start it, which is the default.

The heartbeat above and this are the same nineteen numbers reached two ways, and both are wanted: the heartbeat needs nothing set up and is already in the journal when an incident is reconstructed afterwards; the endpoint can be asked, at whatever rate the person asking chooses, which is the only way transfers_in_flight distinguishes a slow transfer from a stuck one while it is still happening.

Off unless asked for, for the same reason [ServeConfig::http_bind] is: a second listening port is an operator’s decision. See crate::scrape for why it carries no authentication and what that does and does not expose.

§replication_view: ReplicationView

Where replication’s counters are published to, for the console band, the staleness alarm and Admin.Stats.

It is on the config rather than returned from Server::start because the console band starts BEFORE the server does — run hands the same view to both, and start drops the replicator into it once there is one. Left at its default it reports “nothing configured”, which is what a server with no standby is.

§tenants_view: TenantView

Where the band’s tenants row is read from, the same shape and for the same reason as replication_view: a metered Server::start fills it from its entitlement slot; a community build never does, and the band draws no row.

§flow_view: FlowView

Where the band’s flow row is read from. Filled on every build — counting what moved is not the metered appliance’s capability. See crate::dashboard::FlowView.

§wire: WireConfig

What the two git state machines serve under (gunnar-wire’s own Config types, held once — see WireConfig).

Defaults to gunnar-wire’s defaults, which is what every deployment silently ran on while with_config had no callers. The knobs it carries were fully implemented in both protocol versions and reachable from nowhere; --no-filter in particular is the difference between an appliance that declines partial clone and one that only says it might.

§disk_floor: DiskFloor

The disk-headroom floor (--disk-floor): the community plan gate, first link of every front door’s chain and the guard on Repos.Create. See gunnar_ssh::headroom. Always present — the default is 5 % or 2 GiB, whichever is larger — because a box with no floor is a box that finds out it is full from a torn pack.

§space_probe: Option<Arc<dyn SpaceProbe>>

What measures the data set for the floor and the space ledger. None — every deployment — is one statvfs on the data root. A test that must fill a data set of a stated size without a filesystem of that size hands its own probe here (tests/push_space_admission.rs’s virtual disk); nothing else about the server changes.

Implementations§

Source§

impl ServeConfig

Source

pub fn ephemeral() -> Self

A configuration whose listeners take kernel-assigned loopback ports. A constructor for tests, not a mode: nothing about what is authenticated or dispatched changes.

Trait Implementations§

Source§

impl Clone for ServeConfig

Source§

fn clone(&self) -> Self

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 ServeConfig

Source§

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

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

impl Default for ServeConfig

Source§

fn default() -> Self

Returns the “default value” for a type. 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> 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> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

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

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
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> 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 = !

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<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