This repository has been archived on 2026-10-18. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
OpenMFA/deploy/unraid/openmfa.xml
T
Miles WardandClaude Fable 5.1 ad3720edbf v0.1: OpenMFA — live TOTP delivery to Discord, Slack or Telegram
Platform-neutral successor to Discord Authenticator, forked from its v0.1.2
state with fresh history. Same vault, same UI, same live-message mechanism —
now behind one `Sink` that speaks three platforms.

Why each platform needs what it needs (this drove the whole settings model):
- Discord: an incoming-webhook URL. Webhooks can edit their own messages.
- Slack: a bot token (xoxb-, scope chat:write) + channel ID. Slack incoming
  webhooks can post but never edit, and editing IS the live mechanism, so
  chat.postMessage / chat.update via a bot is the only path.
- Telegram: a bot token + chat id. No webhook concept exists. The app detects
  the chat id itself from the bot's pending updates ("send /start, then
  Detect"), since users rarely know it.

- core: Provider enum; settings hold all three configs so switching back and
  forth never re-asks for tokens; per-platform shape validation that names the
  common mistakes (xoxp- user token, #name instead of channel ID); a
  destination key that clears message ids on platform/channel change but not
  on token rotation
- delivery: shared DeliveryError + pacing Governor; Discord client refactored
  onto them; new Slack client (classifies Slack's ok:false-on-HTTP-200
  errors) and Telegram client ("message is not modified" is success; chat
  detection via getUpdates); provider-agnostic message builders — code in a
  copyable span everywhere, rollover time as live as each platform allows
- http-api: setup accepts an optional provider; settings PUT/GET carry the
  full model; /api/slack/whoami, /api/telegram/whoami, /api/telegram/chats
- webui: three-way optional picker on setup and in Settings → Delivery with
  guided per-platform fields; "Check token" (Slack) and "Detect chat"
  (Telegram); inline white mark via currentColor
- brand: white mark on the dark plate; blue stays with the Discord sibling
- docs/TEAMS.md: exploration for v0.2 — Teams connectors were disabled May
  2026 and Workflows webhooks can't edit, so live tiles need a Bot Framework
  bot; options for the inbound endpoint on a homelab and a recommended scope

Verified against mock Discord, Slack and Telegram servers speaking each
platform's real protocol shape: edits target the same message rather than
re-posting, deleted tiles are recovered, dead credentials are surfaced with a
next step, renames refresh without a rollover, and switching platform reposts
on the new one and leaves the old one alone. 138 tests.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-09 00:23:45 -04:00

80 lines
4.0 KiB
XML

<?xml version="1.0"?>
<!--
Unraid Docker template for OpenMFA.
Drop this into /boot/config/plugins/dockerMan/templates-user/ on the Unraid
box, or add the repo as a template repository under Docker → Add Container.
Bridge networking is correct: OpenMFA makes outbound HTTPS calls to the chat
platform you choose and serves one HTTP port.
Web UI: http://<unraid-ip>:8720/
-->
<Container version="2">
<Name>OpenMFA</Name>
<Repository>gitea.milesward.dev/mward4/openmfa:latest</Repository>
<Registry>https://gitea.milesward.dev/mward4/-/packages/container/openmfa</Registry>
<Network>bridge</Network>
<MyIP/>
<Shell>sh</Shell>
<Privileged>false</Privileged>
<Support>https://gitea.milesward.dev/mward4/OpenMFA/issues</Support>
<Project>https://gitea.milesward.dev/mward4/OpenMFA</Project>
<Overview>
Self-hosted TOTP authenticator with live delivery to Discord, Slack or
Telegram.
Add accounts by uploading a QR code, scanning with the camera, pasting an
otpauth:// link, or importing a Google Authenticator export (one export QR
carries many accounts; you get a checklist, and duplicates are detected).
The web UI lists every account with its rotating code, the way Google or
Microsoft Authenticator does.
The distinctive part: each account keeps ONE message in your chosen
channel, rewritten in place every time the code rolls. Three accounts means
three live tiles that keep themselves current. Codes are posted as copyable
text; on Discord a rendered WebP tile is attached as well.
Pick the platform during first-run setup or later in Settings. Discord
needs only a webhook URL. Slack needs a bot token (its webhooks can't edit
messages). Telegram needs a bot token, and the app detects your chat ID for
you.
SECURITY NOTE: anyone who can read that channel can read these codes, which
is most of the point of a second factor. Use a private channel only you are
in, and treat the webhook URL or bot token as a secret. TOTP secrets are
encrypted at rest with XChaCha20-Poly1305.
</Overview>
<Category>Tools:Utilities Security:</Category>
<WebUI>http://[IP]:[PORT:8720]/</WebUI>
<TemplateURL/>
<Icon>https://gitea.milesward.dev/mward4/OpenMFA/raw/branch/main/docs/brand/icon-256.png</Icon>
<ExtraParams/>
<PostArgs/>
<CPUset/>
<DateInstalled/>
<DonateText/>
<DonateLink/>
<Requires>
One of: a Discord incoming-webhook URL; a Slack app with the chat:write
bot scope plus a channel ID; or a Telegram bot token from @BotFather. All
can be added after install from Settings → Delivery.
</Requires>
<Config Name="WebUI Port" Target="8720" Default="8720" Mode="tcp"
Description="Port for the web UI and API."
Type="Port" Display="always" Required="true" Mask="false">8720</Config>
<Config Name="Data" Target="/var/lib/openmfa"
Default="/mnt/user/appdata/openmfa"
Mode="rw"
Description="Vault, encryption key, admin account and settings. BACK THIS UP — losing it loses every stored TOTP secret."
Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/openmfa</Config>
<Config Name="Master key (optional)" Target="OPENMFA_MASTER_KEY" Default=""
Mode=""
Description="Base64 of 32 random bytes. Set this to keep the vault key OFF the appdata share, so a leaked backup of the share is useless on its own. Generate with: openssl rand -base64 32. Leave blank to auto-generate a key file inside the data folder instead. WARNING: if you set this later, existing secrets become unreadable — export them first."
Type="Variable" Display="advanced" Required="false" Mask="true"></Config>
<Config Name="Log filter" Target="OPENMFA_LOG" Default="info,openmfa=info"
Mode=""
Description="tracing-subscriber EnvFilter expression. Use info,openmfa=debug for more detail."
Type="Variable" Display="advanced" Required="false" Mask="false">info,openmfa=info</Config>
</Container>