Files
Miles WardandClaude Opus 4.8 f33effe20a v0.1: self-hosted TOTP authenticator with live Discord delivery
Rust workspace serving a Google/Microsoft-Authenticator-style web UI, with
each account's rotating code mirrored into Discord as a message that
rewrites itself in place as the code rolls.

The live mechanism is webhook message editing: POST ?wait=true returns the
message id, PATCH .../messages/{id} rewrites it thereafter. Two approaches
were evaluated and rejected — an externally-hosted image URL is cached by
Discord's media proxy and, worse, must be anonymously reachable, which would
publish live codes on an unauthenticated endpoint; and an animated countdown
drifts out of phase because playback starts at decode time, not on a wall
clock. The countdown instead uses Discord's <t:unix:R> markup, which the
client re-renders every second for free.

Codes are also sent as text in a fenced block so they stay copyable, with
the grouped, readable form on a rendered WebP tile.

- core: RFC 6238 TOTP over HMAC, lenient otpauth:// parsing (issuer conflicts
  and non-canonical base32 both occur in the wild), XChaCha20-Poly1305 vault,
  Argon2id admin auth
- discord: webhook client with a serialising rate limiter (~2 req/s) so
  globally-aligned rollovers don't burst, and error classification that
  distinguishes a deleted message (repost) from a dead webhook (stop)
- render: SVG -> WebP tiles via resvg with embedded JetBrains Mono
- http-api: axum, session auth, QR decode with a rescale/contrast/rotation
  retry ladder
- webui: offline vanilla JS, Beautiful UI design language, light and dark

Ships as a 27 MB Alpine image (about 8 MB to pull); the binary is fully static musl with no
OpenSSL and no C dependencies, cross-compiled via cargo-zigbuild so building
linux/amd64 on Apple Silicon needs no QEMU.

101 tests, including the RFC 6238 vectors, QR round-trips, and the live push
cycle exercised against a mock Discord.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-28 16:06:08 -04:00

76 lines
2.5 KiB
Markdown

# 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 push
- `write:package` — to push the container image
Export it once per shell session:
```bash
export GITEA_TOKEN='<paste-your-token>'
```
## 1. Create the repository
```bash
curl -s -X POST "https://gitea.milesward.dev/api/v1/user/repos" -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" -d '{"name":"Discord-Authenticator","description":"Self-hosted TOTP authenticator with live Discord delivery","private":true,"auto_init":false}'
```
`"private": true` is deliberate. The repo is harmless on its own, but this is
tooling that handles second factors — there's no reason to publish it by
default. Change it if you decide otherwise.
## 2. Push the code and the tag
```bash
cd "/Users/casperadmin/Discord Authenticator" && 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/discord-authenticator:0.1` and `:latest`.
```bash
printf '%s' "$GITEA_TOKEN" | docker login gitea.milesward.dev --username mward4 --password-stdin && docker push gitea.milesward.dev/mward4/discord-authenticator:0.1 && docker push gitea.milesward.dev/mward4/discord-authenticator: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:
```bash
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/discord-authenticator: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/discord-authenticator` to `/mnt/user/appdata/discord-authenticator`,
publish port `8710`, and leave networking on bridge.
Or import `deploy/unraid/discord-authenticator.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:
```bash
unset GITEA_TOKEN
```