DNS and certificates¶
Direct TLS does not require Advanced Certificate Manager. Client Traefik obtains and renews Let's Encrypt certificates; Cloudflare provides authoritative DNS. Configure an ACME contact for the installation.
Routing¶
| Purpose | Name | DNS and TLS |
|---|---|---|
| Control host | headscale.example.com |
A → 192.0.2.10, DNS-only, direct TLS |
| Client production | <slug>.oduflow.sh |
A → client VM, DNS-only, Traefik HTTPS |
| Client Oduflow | oduflow.<slug>.oduflow.sh |
Client wildcard A, Traefik HTTPS |
| Client IDE | ide.<slug>.oduflow.sh |
Client wildcard A, Traefik HTTPS |
| Branch/service route | <name>.<slug>.oduflow.sh |
Client wildcard A, Traefik HTTPS |
Create <slug>.oduflow.sh and *.<slug>.oduflow.sh with
proxied=false. Publish AAAA only when IPv6 actually works. TCP 80 supports
HTTP-01 issuance and HTTPS redirects; TCP 443 serves Traefik. Administrative Salt
ports remain inside the VPN. Existing managed Oduflow/IDE routes cannot be
replaced by custom routes. This does not reserve a fixed list of names across the
client namespace; quotas limit counts, not specific devN or svcN names.
DNS-only is deliberate: browsers see Traefik's certificate directly. With Cloudflare proxy enabled, Cloudflare presents its own edge certificate. Universal SSL on a full zone normally covers the zone apex and one subdomain level, not arbitrary deeper client hosts. An origin Let's Encrypt certificate does not extend Cloudflare edge coverage. Universal SSL limits.
Cloudflare Tunnel is not the default client publication path: it introduces a separate browser-to-Cloudflare TLS connection. Total TLS does not issue certificates for Tunnel hostnames. Total TLS, Tunnel routing.
Current Let's Encrypt configuration¶
The implemented client uses HTTP-01 for concrete hostnames, including the production apex and both panels. Wildcard DNS routing does not mean the TLS certificate must itself be a wildcard. No broad Cloudflare API token is delivered to the client. The selected Client Oduflow release manages Traefik and its persistent ACME volume.
A conceptual static resolver configuration is:
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: admin@example.com
storage: /var/lib/traefik/acme.json
httpChallenge:
entryPoint: web
The actual paths and container routing are generated by client Oduflow. ACME state
must persist across restarts, remain private (0600) and never be copied into a
golden image with client certificates or keys. Readiness verifies certificate
trust/hostname and application authentication, not merely a running Traefik process.
See production publication for the temporary host ingress
guard and post-hardening ACME retry.
Traefik ACME.
Optional future wildcard certificates¶
A certificate for <slug>.oduflow.sh and *.<slug>.oduflow.sh would
require DNS-01. Wildcards cover one additional label; *.*.oduflow.sh is invalid.
Traefik/Lego can create _acme-challenge TXT records using its cloudflare
provider. Such a design is not enabled by the current client role.
A dedicated DNS-01 token would need Zone / Zone / Read and Zone / DNS / Edit
scoped to the intended zone, supplied through a private file such as
CF_DNS_API_TOKEN_FILE. A separate CF_ZONE_API_TOKEN can supply zone lookup.
Zone scope does not isolate one client's subdomain, so distributing the control
plane's zone-wide token is inappropriate. A delegated challenge zone/CNAME or
central certificate issuer would need a separate design.
Lego Cloudflare provider,
Traefik DNS challenge.
Development route compatibility¶
Check the generated hostname when creating a development environment. The historically tested Oduflow 1.75 branch mode appended the team hostname; an explicit short hostname was needed for a route directly under the client domain. Do not infer the final address from a branch name alone. Use the selected release's configuration contract and verify its actual DNS/TLS route.
Odoo contract and migration¶
Earlier nested snapshots can retain paseo.<slug>.<domain>. Use the recorded
instance address or the explicit IDE hostname migration;
new defaults do not change an existing instance.
New instances use oduflow.sh, naming_version=nested_v2 and
ingress_mode=direct_tls. Production uses <slug>.<domain>, Oduflow uses
oduflow.<slug>.<domain> and IDE uses ide.<slug>.<domain>. The client owns
*.<slug>.<domain>; its Oduflow chooses names within that namespace. The control
plane does not reserve individual devN/svcN slots.
Preparation freezes naming version, ingress, ACME contact, client domain and panel
addresses. Pillar remains schema=1; nested_v2 environment/service lists are
empty and wildcard identifies the owned subtree. Numeric quotas do not assign
DNS names. Direct TLS needs neither a tunnel token nor cloudflared.
Migration 19.0.1.2.0 assigns legacy rows flat_v1 and tunnel before model
initialization. Existing <slug>-devN.<domain> / <slug>-paseo.<domain> names and
base_domain remain unchanged. Prepared snapshots receive their historical DNS
set; new defaults must not rewrite old addresses.
Wildcard namespace reservations cover the whole client subtree, including its
apex, even though a TLS wildcard covers only one label. Conflict checks include
exact names and nested subtrees across companies. Every Prepare transaction updates
one private oduflow.dns.namespace.guard row before checking conflicts. Under
REPEATABLE READ, PostgreSQL rejects a stale concurrent snapshot and Odoo retries.
An advisory lock alone would not make that snapshot fresh. Reservations survive
infrastructure deletion; an infrastructure hostname cannot be reused as a client slug.
Migration 19.0.3.1.0 removes separate fourth-level reservations only when the same
instance owns the apex and wildcard. Legacy flat_v1 records remain. The client
form no longer exposes a separate Reserved Hostnames page.