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]>
2.3 KiB
Publishing to Gitea
Everything here needs a Gitea personal access token, which is why it isn't automated: the token should only ever exist in your own shell.
Create one at Gitea → Settings → Applications → Generate New Token with scopes:
write:repository— to create the repo and pushwrite:package— to push the container image
Export it once per shell session:
export GITEA_TOKEN='<paste-your-token>'
1. Create the repository
curl -s -X POST "https://gitea.milesward.dev/api/v1/user/repos" -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" -d '{"name":"OpenMFA","description":"Self-hosted TOTP authenticator with live delivery to Discord, Slack or Telegram. Container-native Rust.","private":false,"auto_init":false}'
The repo is public so Unraid can pull the image anonymously without registry credentials; the Gitea instance itself is not exposed beyond the LAN.
2. Push the code and the tag
cd "/Users/casperadmin/OpenMFA" && git -c "http.extraHeader=Authorization: token $GITEA_TOKEN" push -u origin main --follow-tags
That pushes main and the v0.1 tag together.
3. Push the container image
The image is already built and tagged locally as
gitea.milesward.dev/mward4/openmfa:0.1 and :latest.
printf '%s' "$GITEA_TOKEN" | docker login gitea.milesward.dev --username mward4 --password-stdin && docker push gitea.milesward.dev/mward4/openmfa:0.1 && docker push gitea.milesward.dev/mward4/openmfa:latest && docker logout gitea.milesward.dev
If the image isn't built (a fresh machine, say), scripts/build-and-publish-unraid.sh
does the clone, build, login and push in one pass:
GITEA_TOKEN="$GITEA_TOKEN" ./scripts/build-and-publish-unraid.sh
4. Deploy on Unraid
Add a container with Repository set to:
gitea.milesward.dev/mward4/openmfa:0.1
Since the package is in a private registry, add the Gitea credentials under Unraid's Docker → Registries first, or the pull will 401.
Map /var/lib/openmfa to /mnt/user/appdata/openmfa,
publish port 8720, and leave networking on bridge.
Or import deploy/unraid/openmfa.xml into
/boot/config/plugins/dockerMan/templates-user/ to get all of that prefilled.
Afterwards
Confirm the token isn't left lying around in your shell history:
unset GITEA_TOKEN