--- # --------------------------------------------------------------------------- # Rootless Podman / Quadlet # --------------------------------------------------------------------------- # Duplicated on purpose from caddy/vars/default.yml: a play cannot include that # file wholesale, because it also sets `host_directory` — to the Caddy data # directory — and every playbook defines that for itself. Keep these three in # sync with caddy/vars/default.yml. (Its fourth, podman_unprivileged_port_start, # belongs to the podman tasks, which this play does not run: the bridge # publishes nothing host-side.) # # podman_user is derived from the connection: this play carries no become, so # the Quadlet units belong to whoever Ansible connects as. Requires gather_facts # (set in playbook.yml). podman_user: "{{ ansible_facts['user_id'] }}" podman_data_dir: "{{ ansible_facts['user_dir'] }}/podman-data" quadlet_dir: "{{ ansible_facts['user_dir'] }}/.config/containers/systemd" # --------------------------------------------------------------------------- # sms-wa-bridge # --------------------------------------------------------------------------- host: bridge.tanshu.dev container_name: sms-wa-bridge unit_name: "{{ container_name }}.service" # The Quadlet file is .network, and caddy/roles/register pins the # Podman network name to _net — so: sms-wa-bridge.network, # sms-wa-bridge_net, which is the name the Docker-era deployment used. network_name: "{{ container_name }}" # There is no registry: the image was built locally on monoco with # `docker build -t sms-wa-bridge` and exists nowhere else. Two ways to get it # onto emily, and the playbook supports both: # # * `docker save sms-wa-bridge:latest | podman load` — the recommended route, # and the one readme.md documents. It carries over the *exact* image whose # WhatsApp session was paired; a rebuild re-resolves whatsapp-web.js # (^1.26.0 in package.json), and a differently-versioned client can refuse # that session and force a new QR scan. # * `podman build` from the vendored context in files/build/ — what the play # does by itself when no image is present, so a host with no monoco still # gets a working deployment. # # Fully qualified either way: rootless Podman resolves short names against # unqualified-search-registries, and there is no TTY to answer a prompt when a # systemd unit starts. The unit also sets Pull=never, so an image that is not # there fails loudly instead of silently becoming a Docker Hub lookup. image: localhost/sms-wa-bridge:latest # What `podman load` actually calls a docker-saved unqualified tag: it qualifies # it against the default registry instead of `localhost`. Verified on emily with # a real `docker save` round trip. The play re-tags this name to `image` above, # so loading the image before the play is enough — see readme.md. image_docker_hub: docker.io/library/sms-wa-bridge:latest # `podman build` is skipped when the image is already present. Set this to true # to rebuild from files/build/ anyway, e.g. after editing server.js: # # ansible-playbook -u tanshu ../sms-wa-bridge/playbook.yml -e rebuild_image=true rebuild_image: false # The whole of this service's state, and a faithful analogue of monoco's # /var/lib/sms-wa-bridge — same four entries, same layout, so the tar in # readme.md lands as-is: # # wwebjs_auth/ the paired WhatsApp session (336 MB of the 336 MB tree) # wwebjs_session/ empty, and vestigial; carried over to keep the shape # rules.json routing rules, re-read from disk on every request # bridge.log the app's own log file host_directory: "{{ podman_data_dir }}/{{ container_name }}" # The image build context, kept out of host_directory so the data tree stays a # faithful copy of the old one. Only used when the play builds the image. build_dir: "{{ podman_data_dir }}/{{ container_name }}-build" # The port server.js listens on inside the container (PORT's default). Caddy # proxies to it; nothing is published on the host, exactly as on monoco. container_port: 8080 # The shared secret MacroDroid sends as `X-Auth`, carried over verbatim from the # Docker-era container so the cutover is a URL change and nothing else. It is # committed in plaintext because it always has been (the old app readme had it, # and the Docker-era container took it as an -e flag); the unit keeps it out of # its own world-readable file via EnvironmentFile=. auth_secret: "64f205031acc01744b6edc23" # AUTH_SECRET lives here rather than as an Environment= line in the unit: that # file is world-readable in ~/.config. Read by podman (--env-file), not by # Ansible, so it has to be written before the unit starts. env_file: "{{ host_directory }}/{{ container_name }}.env" # The image ships no HEALTHCHECK, so the unit defines one and the play gates on # it. /health is the app's own endpoint and `wget` is what the runtime image has # — the Dockerfile installs wget, not curl. It answers 200 whenever the HTTP # server is up, so this proves the server is serving; it does *not* prove # WhatsApp is paired. That is what the /health *payload* shows, and the play # reports it. health_cmd: "wget -q -O /dev/null http://127.0.0.1:{{ container_port }}/health" # --------------------------------------------------------------------------- # The shared Caddy unit and its Caddyfile, both owned by caddy/playbook.yml # --------------------------------------------------------------------------- caddy_name: caddy caddyfile_path: "{{ podman_data_dir }}/{{ caddy_name }}/conf/Caddyfile" # The same two lines monoco's Caddyfile has had since the bridge was deployed # (`bridge.tanshu.dev -> sms-wa-bridge:8080`), so nothing about the public # contract changes but the host it runs on. snippet_block: | {{ host }} { reverse_proxy {{ container_name }}:{{ container_port }} }