Oduflow Platform — technical specification¶
This document describes the full intended workflow; a requirement here is not evidence of a completed deployment. See the README for implementation boundaries and the deployment journal for live checks.
1. Objective¶
An action in the control-plane Odoo creates a dedicated client VM, installs Paseo
and Oduflow, and launches the client's standard Odoo production stack. The result
is working HTTPS at company.oduflow.sh and access to client management panels,
with configured quotas and a demo lifecycle.
The Odoo 19 control plane in Megaflow and the client's Odoo are separate instances. The control plane creates external resources and tracks lifecycle. Client Oduflow manages Docker and launches production inside the client's dedicated VM.
2. Components and ownership¶
| Component | Location | Responsibility |
|---|---|---|
| Odoo 19 control plane and addons | An Oduflow-managed Odoo environment | Clients, plans, queue, provider APIs, Cloudflare DNS, Salt dispatch and lifecycle |
addons/oduflow |
Control-plane Odoo | Provider-independent core and bootstrap contract |
addons/oduflow_vultr |
Control-plane Odoo | Vultr catalogue/API, VM creation, block storage and attachment |
addons/oduflow_cloudflare |
Control-plane Odoo | Per-client R2 bucket, retention locks and bucket-scoped credentials |
| Headscale and Salt master/API | Dedicated Oduflow services or a separate control host | VPN coordination, minion trust and Salt execution |
| Odoo VPN gateway | Persistent Megaflow service | Odoo-to-Salt access and private pillar delivery to the master |
| Tailscale and Salt Minion | Each client VM | Headscale enrollment and client configuration |
| Paseo and Oduflow | Each client VM | Client management; Oduflow runs the production stack |
| Traefik and Let's Encrypt | Each client VM | Application HTTPS |
Cloudflare oduflow.sh zone |
Cloudflare | Authoritative DNS, managed by the control plane |
| GitHub, LLM gateway and email | External services | Repository, model budget and access delivery through separate adapters |
The former addon is merged into addons/oduflow without a compatibility wrapper.
Existing oduflow.* models, tables and records are retained; migration changes
metadata/XML-ID ownership rather than creating replacement client records.
3. Scope¶
The current workflow includes Odoo-driven Vultr VM creation, volume attachment, VPN/DNS, trusted Salt enrollment, app installation, production launch and readiness verification. It does not depend on manually supplying a client server.
Repository provisioning, budgeted LLM credentials, access delivery, expiry, suspension, full client deletion and updates are also part of the platform's lifecycle scope. Their implementation and live validation are tracked separately.
Optional golden-image acceleration through Packer/Vultr is now authorized. Public signup, billing and additional infrastructure providers remain separate work. Client production hosting is already part of the primary workflow.
4. Architectural decisions¶
- One VM per client. Provider parameters and API calls belong in the provider addon; the core owns shared lifecycle behavior.
- Clean Ubuntu 24.04/26.04 amd64 is supported by the bootstrap installer. Optional images preinstall dependencies, while per-client identity and real configuration are generated at first boot. Salt remains authoritative after image creation.
- Odoo keeps immutable preparation snapshots, external resource IDs and a journal. Retries begin with reconciliation. Unknown creation outcomes never authorize blind POST retries. See the dispatch contract.
- Salt manages installed software and client configuration. Initial bootstrap establishes Tailscale/Minion identity; subsequent states configure the same VM.
- A minion uses
client-<UUID>, a pre-registered public key and a pinned master. Arbitrary pending keys are never automatically accepted. - Headscale and Salt master run as dedicated services or on a separate control host. Clients run Tailscale, not their own Headscale server.
- Client publication uses DNS-only Cloudflare records and direct Traefik HTTPS. Let's Encrypt HTTP-01 issues certificates for concrete application hostnames; the wildcard DNS record does not require a wildcard certificate. No Cloudflare DNS token is distributed to clients and Advanced Certificate Manager is not purchased.
- Production creation uses the tested Oduflow 1.75.0 API contract, an empty template
name and
odoo:19.0. Publication is blocked until the administrator credential is hardened and the immutable container identity is verified.
5. Provisioning workflow¶
Preconditions: the control plane has a provider plan, credentials, Headscale/Salt configuration and required application settings. No client VM exists yet.
- Prepare: validate naming/quotas, freeze the plan snapshot, reserve the DNS
subtree, generate UUID/secrets and create journal entries.
plannedcreates no cloud resources. - Prepare bootstrap: generate the client's unique RSA identity. Before VM creation, register its exact Salt public key and obtain a non-reusable Headscale enrollment key. Validate renderable user-data; never log or bake in secrets.
- Create infrastructure: create and reconcile the VM and volume, preserve
provider IDs and confirm attachment. The instance remains
provisioning. - Connect management: first boot installs/enrolls Tailscale and Salt Minion,
checks the master pin and becomes reachable as the expected
client-<UUID>. - Publish DNS: create DNS-only
company.oduflow.shand*.company.oduflow.shrecords pointing to the verified client IPv4. - Configure the client: Salt verifies and mounts owned XFS storage, installs dependencies and configures Paseo/Oduflow. Data and services depend on the verified volume; an unknown disk must never be formatted.
- Create production: close host ingress before Docker DNAT for both IP families, record a publication guard, call client Oduflow, set the Odoo administrator password and verify the resulting immutable container ID. Then restore only the guard's own rules and retry ACME when necessary.
- Back up client data: the control plane creates a dedicated R2 bucket and exact-bucket credential. Salt stops data-owning services, backs up the verified block-volume mount, restarts services and restores an identity-bound probe from the resulting Restic snapshot. It enables the daily timer only after the byte-identical restore and repository check succeed.
- Verify readiness: check valid HTTPS, Odoo 19 administrator login/logout and unauthenticated/authenticated panel behavior. Only the appropriate lifecycle completion step may activate the client and start its demo period.
GitHub and LLM credentials are prepared before the steps that need them. Missing required credentials cause an explicit failure, never a fabricated working value. Basic app installation does not require an invented LLM key or license.
infra_state=ready confirms the VM and disk; Salt ping confirms connectivity.
Neither substitutes for production checks. Retry applies to the existing VM,
not a replacement created for every Salt-state correction.
6. Network and names¶
| Purpose | Hostname |
|---|---|
| Headscale / Salt control host | headscale.example.com |
| Client production Odoo | <company>.oduflow.sh |
| Client Oduflow panel | oduflow.<company>.oduflow.sh |
| Client Paseo panel | paseo.<company>.oduflow.sh |
| Intended branch route | <branch>.<company>.oduflow.sh |
| Additional service | <service>.<company>.oduflow.sh |
The client owns its entire DNS subtree. Panel names cannot be reused by branch
routes. Control-plane quotas limit resource counts, not a static collection of
devN/svcN names. Prepared flat_v1 instances keep their legacy addresses.
Oduflow 1.75 requires explicit route hostnames for branch routes directly below
the client domain; implicit branch naming can add the panel's extra label.
Ports 80/443 reach client Traefik for HTTP-01 and HTTPS. Cloudflare proxy/Tunnel is not enabled automatically: an origin certificate does not provide Cloudflare edge coverage for deep hostnames. Salt 4505/4506, Salt API and pillar are VPN-restricted. Clients cannot access Salt API, other clients or the pillar gateway.
7. Pillar and secrets¶
GET /oduflow/pillar/<minion_id> returns JSON schema=1 only for eligible
instances with required configuration. It checks the original gateway socket peer
and Bearer token; forwarded headers cannot bypass this. Salt external pillar
validates the UUID and schema.
The contract includes client, dns, naming_version, ingress, oduflow,
paseo and storage, plus a complete backup bundle only for opted-in snapshots.
Direct TLS uses HTTP-01 and the prepared ACME contact
configured for the installation, without tunnel_token, cloudflared or a broad
DNS API token. Separately generated credentials protect Oduflow UI/MCP, PostgreSQL,
Paseo and the production administrator. Complete optional Git/LLM bundles are
validated before use.
Secrets are encrypted using an external environment key stored separately from DB backups. Secret files have restrictive permissions and Salt suppresses their content in changes. Golden images contain no client PKI, enrollment credentials, VPN state, customer data or certificates.
8. Client deletion, updates and storage growth¶
Full client deletion accounts for client workloads, provider VM/volume, DNS,
Salt/Headscale identities, LLM access and repository policy. Preserve journal IDs;
only report destroyed after the relevant cleanup has been confirmed. VM deletion
alone does not establish all external cleanup is complete.
Salt applies pinned application changes to existing VMs. Test an update on one client before wider rollout; changing a running client does not require rebuilding its golden image.
Block storage can grow online through an explicit target-size request. Keep the original preparation snapshot unchanged and record verified current capacity separately. Reconcile provider resize before growing the existing owned XFS mount; never shrink, format, detach or stop services as part of the online grow path. No paid resize is inferred when the operator has not supplied a target.
9. Acceptance criteria¶
Verify Odoo-driven client creation, production HTTPS and authentication, no duplicate resources on retry, reboot recovery, service shutdown on loss of the data volume, and confirmed client deletion. Exercise an opted-in backup and restore, then a separate replacement-VM recovery rehearsal. Billing and additional provider integrations require their own end-to-end checks. Record observed results, source revision and deployment identities in a separate deployment report; unit tests alone do not establish live readiness.