network:proxy service¶
Publishes a module through Caddy — the module's public face. It owns the hostname, the certificate, the upstream handler and the firewall alias that decides who may reach it. Nothing here touches a guest; the changes land on the proxy and the firewall, and the workload never notices.
10 fields — all in-place, all apply: "reconcile".
The module's public face. Nothing here touches a guest: the changes land on Caddy and the firewall, and the workload never notices. The largest drift blind spot — see recommendation 1.
Why reconcile and not set¶
update-service.sh rewrites this module's whole Caddy site block and its OPNsense alias from the declared values on every pass, then reloads. That is already idempotent and already handles removal — a handler that should no longer exist is deleted, which no scalar field diff can express. Flattening it into set fields would lose that. The manifest declares the class, which is what makes --set sanctioned; the apply stays where the domain knowledge is.
Because there is no report-service.sh, module-manager module drift reports these fields as not-reported rather than comparing them — the converge is trusted to have made config true. That is the blind spot recommendation 1 closes.
Fields¶
network:proxy owns 8 declared field(s). Each table below carries the field's full definition and, where the service applies it, its ADR-020 change semantics.
proxyDomain¶
Public domain name for the reverse proxy. Caddy obtains a TLS certificate for this domain.
| Attribute | Value |
|---|---|
| Type | string |
| Default | <vmname>.<tappaas.domain> |
| Format | ^[a-zA-Z0-9](https://codeberg.org/TAPPaaS/TAPPaaS/src/branch/main/src/foundation/network/services/proxy/%5Ba-zA-Z0-9.-%5D%2A%5Ba-zA-Z0-9%5D)?$ |
| Example | vaultwarden.test.tapaas.org |
| Required by | (none) |
| Used by | network:proxy |
| Change class | in-place |
| Apply mode | reconcile |
About the field. If not set, defaults to
Why this change class. The public hostname. Changing it re-issues the ACME certificate and moves the handler; the module itself is untouched, so no guest downtime — though clients on the old name stop resolving as soon as DNS follows.
proxyPort¶
Target port on the module VM that the reverse proxy forwards traffic to
| Attribute | Value |
|---|---|
| Type | integer |
| Default | 80 |
| Minimum | 1 |
| Maximum | 65535 |
| Example | 8080 |
| Required by | (none) |
| Used by | network:proxy |
| Change class | in-place |
| Apply mode | reconcile |
| Normalizer | integer |
About the field. The port the service listens on inside the VM
Why this change class. The upstream port Caddy forwards to. A handler rewrite, applied live.
proxyUpstreamTls¶
Reverse-proxy to an HTTPS upstream instead of plain HTTP. Set true for backends that only speak TLS — e.g. the OPNsense GUI on :8443. network:proxy renders the Caddy upstream as https:// with upstream certificate verification skipped (internal/self-signed backends).
| Attribute | Value |
|---|---|
| Type | string |
| Default | false |
| Allowed values | true — Upstream is HTTPS (skip upstream cert verification)false — Upstream is plain HTTP (default) |
| Required by | (none) |
| Used by | network:proxy |
| Change class | in-place |
| Apply mode | reconcile |
| Normalizer | boolean |
Why this change class. Whether the hop from Caddy to the module is itself TLS. Declared as a string in module-fields.json, so the boolean normalizer is what makes 'true' and true one value.
proxyUpstreamHttp1¶
Force HTTP/1.1 to the upstream (os-caddy HttpVersion=http1). Required for apps whose UI rides a WebSocket behind a TLS upstream — e.g. the UniFi OS console. Without it, Caddy negotiates HTTP/2 with the upstream, which cannot carry a WebSocket Upgrade and returns 500, so the SPA renders blank.
| Attribute | Value |
|---|---|
| Type | string |
| Default | false |
| Allowed values | true — Force HTTP/1.1 to the upstream (WebSocket support)false — Default upstream HTTP versions (HTTP/1.1 + HTTP/2) |
| Required by | (none) |
| Used by | network:proxy |
| Change class | in-place |
| Apply mode | reconcile |
| Normalizer | boolean |
Why this change class. Force HTTP/1.1 upstream, for a backend that cannot speak h2c.
proxyPreserveHost¶
Force the upstream Host header to the public domain (Caddy header_up Host
| Attribute | Value |
|---|---|
| Type | string |
| Default | false |
| Allowed values | true — Send Host: false — Caddy default upstream Host |
| Required by | (none) |
| Used by | network:proxy |
| Change class | in-place |
| Apply mode | reconcile |
| Normalizer | boolean |
Why this change class. Pass the original Host header through, for a backend that generates absolute URLs from it.
proxyTls¶
How network:proxy obtains the public TLS certificate for this domain. OMIT IT to inherit the environment's domains.dnsMode (config/environments/
| Attribute | Value |
|---|---|
| Type | string |
| Allowed values | dns01 — Wildcard certificate via os-acme-client; bound by refid through Caddy CustomCertificate (no per-module ACME, whttp01 — Per-domain ACME HTTP-01 via Caddy itself (no DNS-API needed, but the domain must be reachable from the interne |
| Required by | (none) |
| Used by | network:proxy |
| Change class | in-place |
| Apply mode | reconcile |
Why this change class. Which certificate strategy serves the domain (per-service or the environment wildcard).
proxyAllowedZones¶
Zones (and the literal 'internet') permitted to reach this service through the reverse proxy. network:proxy compiles this into an os-caddy access list (allow-list by client subnet) attached to the handler; non-matching clients get HTTP 403. Under ADR-021 D2 this is the ONLY place a zone restricts a published service: split-horizon DNS returns the same address (the DMZ gateway) to every caller, so the answer encodes no entitlement and the access list is what narrows it.
| Attribute | Value |
|---|---|
| Type | array |
| Default | |
| Example | mgmt, home, work, srvHome, srvWork |
| Required by | (none) |
| Used by | network:proxy |
| Change class | in-place |
| Apply mode | reconcile |
About the field. Zero-trust by default: when omitted, a service is reachable only from the internal trusted zones, never the internet. Add 'internet' to publish it publicly (no restriction). Zone names are resolved to subnets via zones.json. Changing this re-applies on the next install/update of the module. TWO values carry real consequence and the rest is belt-and-braces: leaving it UNSET is the normal, correct configuration for a published service, and adding 'internet' is what makes it publicly reachable. Narrowing to a specific list of internal zones filters IN FRONT OF the identity gate, it is not the mechanism protecting the service (ADR-021 Case ⅓) — a zone list that lets someone through still leaves them at an Authentik login, and one that shuts them out only saves them the round trip. Do not use it to separate two populations who are each entitled to a different environment: that binds entitlement to network position, which breaks the moment a legitimate user connects from another zone (the same person on a laptop in home, a phone on guest wifi and the netbird overlay is ONE identity in three zones). Authentik group membership is where that belongs.
Why this change class. Which zones may reach the published name — the difference between an internal service and one exposed to the internet. A live firewall/Caddy change, and the field most worth being able to set through a verb rather than by hand.
proxyRoutes¶
Additional reverse-proxy routes for a VM that serves several endpoints on different ports. Each entry publishes
| Attribute | Value |
|---|---|
| Type | array |
| Default | (none) |
| Example | [{"name": "admin", "port": 9090}, {"name": "metrics", "port": 3000}] |
| Required by | (none) |
| Used by | network:proxy |
| Change class | in-place |
| Apply mode | reconcile |
About the field. Each entry is { name:
Why this change class. The extra hostnames a single VM publishes. Adding or removing an entry creates or prunes that route live; the primary route and the module itself are untouched.