Network Modes πΒΆ
How a node decides which networks it serves each role on, and how vhosts, TLS, cross-app URLs, checks and CI follow that decision. For general documentation rules such as links, writing style, RFC 2119 keywords, and Sphinx behavior, see documentation.md. For the Tor provider itself see svc-net-tor.
Registry ποΈΒΆ
reachability.py holds one Network entry per network. Every other module asks it (network_of, sibling_domain, network(name)) instead of testing a domain suffix of its own.
Network |
Label |
Provider |
Suffix |
TLS |
Browser proxy |
|---|---|---|---|---|---|
|
|
none |
none |
yes |
no |
|
|
|
|
no |
yes |
handshake is listed in RESERVED_NETWORKS; network('handshake') raises until it is implemented.
Node Modes π§ΒΆ
A node runs in one NETWORK_MODE:
Mode |
Networks served |
|---|---|
|
clearnet |
|
tor |
|
clearnet and tor |
NETWORK_MODE defaults to empty in 08_networks.yml. Empty resolves to multi when svc-net-tor is deployed with a node onion address and to clearnet otherwise. tor or multi on a node without the provider fails the deploy.
Role Reachability ποΈΒΆ
roles/<role>/meta/networks.yml is the single point of truth for the networks one role may be served on:
reachability:
modes: [clearnet, tor]
single_mode: true # nocheck: network-reachability <why the role answers under one domain only>
handover: true # nocheck: network-reachability <why the role answers under its canonical URL only>
modeslists networks from the registry. An empty list or no key means every network.multiis a node mode, not a list value.single_mode: trueserves the role ontorwhen the node runs Tor and onclearnetotherwise, never on both.handover: truekeeps every network sibling, but each sibling outside the canonical network answers302to the same path on the canonical URL and advertises noOnion-Location. The webserver health check expects that302, the CSP crawl skips those siblings and Playwright tests only the canonical domain.single_mode: true,handover: trueand amodeslist below every network each carry# nocheck: network-reachabilityplus a reason on or directly above their line (suppression.md).
test_network_reachability.py enforces the schema and the marker. meta/services.yml keeps only the Tor service bond (bond, enabled, shared).
The effective networks of a role are the nodeβs networks that the role allows, where tor also needs the roleβs services.tor.enabled to render true, reduced to one network by single_mode. A deployed role with no effective network fails the deploy. A role without a tor service keeps its clearnet domains on every node.
Domains and Vhosts πΒΆ
get_merged_domainsin domains.py expands every canonical domain of a role into one sibling per effective network, canonical network first. Lists gain the sibling after the clearnet entry; named dicts gain a<key>_onionentry.lookup('network_siblings', domain)returns the vhosts one domain is served under. sys-stk-front-proxy renders one vhost per sibling, and a non-canonical sibling handed in directly renders nothing, so no vhost is rendered twice.TLS is decided per vhost with
lookup('tls', domain, ...): onion vhosts listen on port 80 in plain HTTP, clearnet vhosts followTLS_MODE.Every app-id term of the
tlslookup is aligned to the network of the vhost being rendered (align_domain_to_consumerin tls_common.py), in both directions. The SSO provider is never aligned: the node publishes one issuer, the canonical one.Injected snippets (sys-front-inj-all) and the CSP header (content_security_policy.conf.j2) are rendered per vhost. Clearnet vhosts of a role served on more than one network send
Onion-Location.Redirect vhosts redirect inside the network of their source domain.
Network-Aligned Values π§ΒΆ
A value that names an own or peer host MUST point to the network of the vhost that serves it. Pick the form by how the value is consumed:
Value |
Form |
How |
|---|---|---|
Allowlist a peer checks (OIDC redirect and post-logout URIs, web origins, Collabora |
Union of every sibling, rendered once |
|
Asset a browser loads (logo, icon, manifest icon, CDN file) |
Same-origin path |
|
Object-store file a browser loads (uploads, media in a public bucket) |
Same-origin path |
|
Object-store file an application signs or keeps private (presigned URLs, Active Storage, django-storages) |
Served by the application on its own origin |
The application streams or |
Pointer a browser follows from a snippet rendered per vhost (injected script, proxy config) |
Rendered per vhost |
|
Own public URL an application keeps in one server-side setting |
Request-derived or one instance per network |
Let the application read |
A role MUST NOT write an onion host by hand or rewrite hosts in its own proxy config.
The SSO issuer is the one host that stays canonical on every network.
Every plain-HTTP vhost strips
Securefrom upstream cookies withproxy_cookie_flags ~ nosecure(nginx_ssl_header.j2). A location that sets its ownproxy_cookie_flagsreplaces this rule for that location.Logout: Keycloak holds one front-channel URL per client, the canonical logout conductor. On a vhost outside the canonical network the injected logout patch adds
client_idandpost_logout_redirect_uriset to that networkβs conductor, and Keycloak registers every conductor sibling as post-logout redirect URI next to+. An external issuer (OIDC_OVERRIDE.URL) keeps the bare logout URL.test_network_allowlist_sinks.py fails when a role template names an allowlist sink without the sibling union. Suppress a single-network peer with
# nocheck: network-allowlistand a reason.
Checks π©ΊΒΆ
sys-ctl-hlth-webserver probes every vhost with that vhostβs scheme.
sys-ctl-hlth-csp crawls the
httpand thehttpsvhosts in one run; hosts under the Tor suffix go through the Tor SOCKS proxy.
CI πΒΆ
network.py derives the node modes a matrix row can take from the roleβs Tor bond,
reachabilityand whether its deploy pulls the Tor provider in; thenetworkinput (auto,clearnet,tor,multi) narrows them. See README.md.A row hands its mode to the deploy as the
networkvariable, which provisioning writes into the inventory asNETWORK_MODE(network_mode.py).make compose-deploy apps=<role> network=tordoes the same locally.test-e2e-playwright runs the roleβs suite once, on the vhost of one network sibling. On a node serving the role on several networks the sibling is picked at random and kept for the flake retry, and network-leak.spec.js joins the run with the guest persona. The leak spec fails when a document requests a host of another network or the browser blocks mixed content; the SSO issuer is the one host every network may reach.
Adding a Network βΒΆ
Add a
Networkentry toNETWORKSin reachability.py with its provider role, suffix, TLS policy, browser-proxy flag and probe timeout, and remove its name fromRESERVED_NETWORKS.Add the node modes that serve it to
NODE_MODES.Extend
sibling_domainwith the way the network derives a host from a clearnet one.Gate the network on its providerβs service bond in
allowed_networks, astoris gated onservices.tor.enabled.Add a glyph for each new node mode to symbol_glossary.py.