Nginx Routing

MultiAgent never spreads requests by itself: each server runs its own Nginx container agents-nginx, which MultiAgent deploys, configures and updates. All of a server’s applications — agents, products, tools with a web interface and arbitrary services — are served from subfolders of one address: {base}/{slug}/. No separate domains, subdomains or hand-written configs per application: a server has one address, and every application lives in its own subfolder.

How it works

On every managed server MultiAgent raises one agents-nginx container (the nginx:alpine image, service name nginx). The container is installed from the server page’s Nginx tab: MultiAgent creates the config files on the server (uploaded over a secure channel), joins the container to the shared Docker network and starts it.

The routing config is assembled from the MultiAgent database automatically: on every change (an agent, product or tool record created, changed or deleted; a link or custom route added or edited; central login installed or removed; ports changed) the config is rebuilt and applied on its own. Applying never drops connections: the configuration is validated and reloaded inside the already-running web server, so open pages and live chats stay alive (the container is re-created only when its own settings file changes). Changing the server’s address (host or URL template) also rebuilds the routes and additionally syncs the central-login settings. Installs show their steps in the progress feed; record changes apply in the background. The “Apply configuration” button in the tab runs the same update by hand — handy when an edit was made right on the server.


Where routes come from

The shared routing table is assembled automatically from five sources:

  • Agents and products — every deployed agent record (catalog products included) gets a route automatically: it appears with the record and disappears with its removal;
  • Extra links of agents and products — a link from the Links tab with a route set gets its own subfolder, leading to the chosen server port: it appears with the link and disappears with it;
  • Tools with a web interface — a tool whose template declares a web port gets a route automatically (the port comes from the template and refreshes on every deployment); without a web port the tool stays at the direct address http://{host}:{port};
  • Central login — a derived route with the fixed slug auth (the address {base}/auth/ — the login page): it appears when central login is installed from the Auth tab and disappears when it is removed; read-only, never gated, and it has no toggle of its own;
  • Custom routes — the only manually managed kind: any http(s) address (it does not have to be managed by MultiAgent — a service on its own port, a local system, an external one), with editing, disabling and deletion.

The tab lists all routes sorted by slug, with the public address and the target for each; the automatic kinds are marked “Managed by MultiAgent” and a click leads to the source record (clicking the central-login row opens the server page’s Auth tab).


Traffic path

Traffic flows like this: http(s)://{serverHost}/{slug}/ → the server’s Nginx → the application’s container on the internal Docker network ({container name}:{port}). Applications are addressed by container names, so nothing needs to publish ports to the outside.

  • Resolving on the fly — the config uses the internal resolver and variable addresses: Nginx starts and works no matter which applications exist. A broken, stopped or not-yet-deployed application fails only on its own subfolder; the other routes keep working;
  • WebSocket and large responses — persistent connections (the agents’ live chats), long timeouts and unlimited transfer size are supported;
  • Headers — the application receives service data about its prefix, host and scheme, so prefix-sensitive applications build correct absolute addresses; behind an external proxy, applications get the https scheme and the original host;
  • The root / — served by the server’s home page when one is installed (see below); without it the root behaves as a plain fallback page.

Protecting applications (central login)

When central login is installed on a server (the server page’s Auth tab; details — on the Servers page), protected routes pass through it: a visitor signs in once at the {base}/auth/ login page and then opens any protected application of that server without logging in again.

  • Agents and products are closed behind central login by default — a toggle in the application’s card can open them back up. Tools with a web interface and custom routes are protected by your choice: the “Behind SSO” toggle on the row. The login route itself (auth) is never gated;
  • Protection needs HTTPS: it turns on only when the front works over HTTPS — a local certificate and the HTTPS port from the Nginx tab, or a URL template of the form https://{base}/{slug} when TLS is terminated by an upper reverse proxy. Otherwise an installed central login stays inactive;
  • Machine paths are never gated: /{slug}/api/… and /{slug}/svelteChatHub work without signing in (the applications run their own auth there — JWT or API key), and direct access at host:port, bypassing nginx, keeps the application’s own login. A stopped central login closes only its protected routes — the other subfolders keep working;
  • Identity and the secret: a protected application receives the visitor’s identity headers (name, groups, email), and agents and products additionally the X-SSO-Token secret, by which they make sure the request came through nginx (checked against SSO_PROXY_TOKEN in the agent’s environment variables); tools and custom routes do not get the secret.

The namespace and reserved prefixes

The first-level slug is one namespace per server, shared by agents, products, tools and custom routes: the same slug may exist on different servers, but on one server it can be claimed only once. Slugs are lowercase letters, digits and hyphens only ([a-z0-9-]+).

MultiAgent’s own service paths are reserved and can never become slugs: api, v1, swagger, servers, agents, tools, workspaces, analytics, accounts, data, licenses, models, skills, forgejo, plus a few technical ones (static, _app, manifest.json, the SignalR hubs) — and the slug auth, reserved on every server whether or not central login is installed, belonging to its route. An agent named api, servers or auth cannot be created — the form flags it immediately, and the server rejects the record.


Custom routes

A custom route points a subfolder at any absolute http(s) address reachable from the server: a service on its own port, a local application, an external system. Creating one takes a name, a slug under the namespace rules, and a target — an absolute http(s)://… address (a value without a scheme is rejected).

  • Every change (create, edit, enable, disable, delete) immediately rebuilds and applies the config;
  • A disabled route removes its subfolder — it starts answering 404, while the record stays in the table;
  • Deleting frees the slug for any kind — another agent, tool or route can claim it;
  • If the target has its own base path (for example http://host/wiki), it replaces the slug: /{slug}/docs/page reaches /wiki/docs/page.

Certificates

Certificates are managed from the same Nginx tab — three kinds:

  • Self-signed — generated by MultiAgent and serving as the fallback certificate; you can download it as a .crt file and import it into your system’s trusted certificates;
  • Your own PEM pair — pasted or uploaded; the chain and the key are validated before saving (the key must match the certificate; an expired one is rejected with a warning);
  • Let’s Encrypt — free certificates through Nginx itself (domain ownership checked by the ACME protocol): a staging (test) and a production endpoint, one order at a time, the domain must point at the server.

Key material lives only on the server (the ssl/{domain}/ folder: full chain + private key) and is mounted into the container read-only — only metadata sits in the MultiAgent database. Every config push renews certificates expiring within 30 days (self-signed ones regenerate, Let’s Encrypt is requested anew), plus there is manual per-row renewal. The HTTPS port is optional: while it is empty, only HTTP is published, and certificates are stored but not served. A successful Let’s Encrypt request with an empty port enables it by itself (443) — the certificate is served right away, while a port you chose or an explicitly disabled HTTPS is never overridden. In local mode (your own certificate and an HTTPS port) all HTTP, including route subfolders, is redirected to HTTPS — the only exception is /.well-known/acme-challenge/, which Nginx keeps answering itself. When TLS is terminated by an upper reverse proxy (the https://{base}/{slug} URL template), the HTTP listener keeps serving the routes — the browser already talks HTTPS to the proxy.


MultiAgent itself in a subfolder

MultiAgent is such an application itself: it can live not only at a domain’s root but in any subfolder (your-domain/multiagent), the same way and without a rebuild. Nothing needs configuring inside the program — MultiAgent understands where it is from the request address. There are two ways to tell it.

Through an environment variable — when the upper proxy passes the path as-is, unchanged. Just add at startup:

- MULTIAGENT_BASE_PATH=/multiagent

MultiAgent strips the prefix itself and once redirects requests typed without it to the subfolder address. The certificate check path (/.well-known/) stays at the root — so issuing keeps working. An empty value means running at the domain root.

Through a proxy header — when the subfolder is owned by the proxy itself: in its config, one line is added to the usual proxying — proxy_set_header X-Forwarded-Prefix /multiagent;. MultiAgent then reads the subfolder from the header and redirects nothing — routing belongs fully to the proxy.

Deploying on a managed server needs none of this: the configuration MultiAgent builds for it already passes the subfolder and the needed header. One domain can host several instances in different subfolders: each has its own database and its own keys, so a sign-in in one never works in another, and in the browser each subfolder keeps its own separate state.


The server home page

Nginx’s root / serves the server home page when one is installed (the server page’s “Home page” tab; requires Nginx installed). It is a static card board that MultiAgent builds from its own database and uploads to the server: one card per agent, product and tool with its address — with the projects’ real logos, plus a Git card (when Forgejo is installed) and a Logout card. The page has no login of its own: with central login on, it is closed together with the other applications. Details — on the Server Home Page page.

Application subfolders always take precedence over the page’s paths: an agent with the slug settings is served at /settings/, while the home page itself keeps working at the root. If Nginx is stopped, only the root suffers — the application subfolders keep working.


The public address

Every application’s canonical public address is {base}/{slug}/. The base is derived in one place: it is the base of the agents’ URL template, when that template has the exact form https://{base}/{slug} (this is how a proxied server gets https links without its own certificate); otherwise it is http(s)://{serverHost}[:{nginx port}]. The scheme follows one rule: any certificate + an enabled HTTPS port → https, else http. Direct access http://{host}:{port} stays the fallback path and is labeled “direct access” in the interface.

← Back to documentation