/
/
/
Ansible role that deployes services on my runner machine
1# ==============================================================================
2# THE CHAMBERLAIN - session and campaign orchestration
3# ==============================================================================
4# Note what is NOT here: no LIVEKIT_ROOM, and no transcript or recording paths.
5# Those are per-session and the Chamberlain injects them into the containers it
6# starts. This file carries only what is true for every session.
7
8DND_MEDIA_ROOT=/media/dnd
9
10# The compose project it drives. The project name must match the /docker/
11# directory basename or the backup role, which derives the service name from the
12# basename, silently backs up nothing.
13DND_COMPOSE_DIR={{ dnd_config_dir }}
14DND_COMPOSE_PROJECT={{ dnd_config_dir | basename }}
15
16# LiveKit, for creating a room per session and reading who joined at the end.
17# The SFU's websocket URL; the server API is derived from it.
18LIVEKIT_URL={{ livekit_public_ws_url }}
19LIVEKIT_API_KEY={{ vault_runner.livekit_api_key }}
20LIVEKIT_API_SECRET={{ vault_runner.livekit_api_secret }}
21
22# Handed to the TV app by /api/rooms, so the APK holds one address (this
23# service) rather than three. This is the Meet deployment, not the SFU: the
24# token response Meet returns carries the SFU URL itself. Moving either one
25# therefore needs no new APK.
26LIVEKIT_MEET_URL={{ livekit_public_url }}
27
28# Polled until it answers before a session is reported live, so the UI never
29# claims a session is up while the DM page is still starting.
30DND_TABLE_URL=http://dnd-table-runner:{{ dnd_table_http_port }}
31
32# The opening sequence's music, relative to the media root.
33#
34# **A cue, not an ambience bed**, which is why it lives in its own directory
35# rather than beside the 514 Tabletop Audio beds: anything under `ambience/`
36# is a candidate the judge may pick for a scene, and a four-minute orchestral
37# piece arriving because the party walked into a tavern is not the intent.
38#
39# Empty disables the music and leaves the lights and the slideshow, which is
40# the right failure for a deployment that has not been given a track.
41DND_OPENING_TRACK={{ dnd_opening_track }}
42DND_TABLE_READY_TIMEOUT={{ chamberlain_table_ready_timeout }}
43DND_POST_TIMEOUT={{ chamberlain_post_timeout }}
44
45# The mini forge, on the table Pi itself rather than on runner-network -- it
46# drives SPI hardware and cannot run here. Reached by host address; the Pi runs
47# it with host networking.
48#
49# The face crop and the dithering happen HERE, in the Chamberlain, and the Pi
50# receives a finished 88x184 four-colour frame. Empty disables the feature and
51# the cast cards hide the button rather than offering one that cannot work.
52DND_FORGE_URL={{ chamberlain_forge_url }}
53
54# The Almanac: per-player character curation, and the only service holding
55# user accounts. This service administers campaigns and membership through its
56# API; the token never reaches a browser, because the People page calls this
57# service and this service calls the Almanac.
58#
59# Reached by its compose alias rather than the host port: both containers are
60# on the same network, and the alias survives a port change.
61# An inline conditional expression rather than a block tag. Ansible renders
62# templates with trim_blocks, which eats the newline after a block, so the
63# block form ran this value straight into the next line and produced
64# `http://dnd-almanac:8099DND_ALMANAC_TOKEN=...` -- a 502 whose cause was
65# nowhere in the error. A `#` line here is still template text, so avoid
66# writing block tags even inside a comment.
67DND_ALMANAC_URL={{ 'http://dnd-almanac:8099' if dnd_almanac_enabled else '' }}
68DND_ALMANAC_TOKEN={{ dnd_almanac_admin_token }}
69# Where a BROWSER reaches the Almanac, which is not where this service does.
70# The line above is a Docker network name that resolves inside the compose
71# network and nowhere else, so linking a player to it would be a dead link.
72# Empty means the UI names the Almanac without offering a link.
73DND_ALMANAC_PUBLIC_URL={{ dnd_almanac_public_url }}
74DND_FORGE_TIMEOUT={{ chamberlain_forge_timeout }}
75
76# Which chronicler the post container runs. Read only to decide which re-run
77# buttons to offer: the local chain's stages do not exist on the remote path,
78# and offering them would let a press quietly replace a remote chapter with a
79# local one. Must match the post container's own value.
80DND_CHRONICLE_BACKEND={{ dnd_chronicle_backend }}
81
82# Announces the live session on dnd/session/state, retained, so the table server
83# and Home Assistant can scope themselves without asking over HTTP.
84DND_MQTT_HOST={{ mqtt_host }}
85DND_MQTT_PORT={{ mqtt_port }}
86DND_MQTT_USERNAME={{ mqtt_username }}
87DND_MQTT_PASSWORD={{ mqtt_password }}
88DND_BASE_TOPIC=dnd
89
90CHAMBERLAIN_HTTP_PORT={{ chamberlain_http_port }}
91
92# --- Email -------------------------------------------------------------------
93# Recap and invitation, sent only when asked. With no host set the UI hides the
94# buttons and explains what is missing rather than failing on press.
95DND_SMTP_HOST={{ chamberlain_smtp_host }}
96DND_SMTP_PORT={{ chamberlain_smtp_port }}
97DND_SMTP_USERNAME={{ chamberlain_smtp_username }}
98DND_SMTP_PASSWORD={{ chamberlain_smtp_password }}
99DND_MAIL_FROM={{ chamberlain_mail_from }}
100DND_MAIL_TO={{ chamberlain_mail_to }}
101
102# --- The test feeder ---------------------------------------------------------
103# Recorded episodes, for feeding a real session's material through the deployed
104# system. The UI hides the panel entirely when this directory holds none, so a
105# deployment without test material shows nothing rather than an empty box.
106DND_TESTFEED_DIR={{ dnd_testfeed_dir }}
107
108# A full episode is about three and a half hours and a run at 1x takes that
109# long by design: the pipeline is not clock-scaled, so publishing faster does
110# not make it think faster. Generous enough for the longest episode without
111# letting a wedged run sit forever.
112DND_FEED_TIMEOUT={{ dnd_feed_timeout }}
113
114# Recorded in each run's header, so a cues.jsonl can be traced back to the
115# image that produced it -- which is what makes two runs comparable after a
116# threshold change.
117DND_AGENT_IMAGE={{ dnd_agent_image }}
118