It showed node health, addresses, topology, wallet, stamps and chain state --
the same ground bee-manager covers at stamps.ffaerber.duckdns.org, and covers
better: bucket maps, top-up and dilute actions, the action ledger, alerting.
Two views of one node drift apart, and the weaker one is the one people find
first, from the gateway's own nav.
Route, component and nav entry go together. The gateway's /bee/stats endpoint
is left alone: nothing else in this app called it, but it is a public API route
and removing it is a decision about the API, not about this page.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYFnHida4AdiNDek2pzgXx
The build args were only in development-dependencies-env, which runs npm ci.
npm run build happens in build-env, and args do not cross stages -- so Vite has
never seen VITE_API_URL, VITE_GOOGLE_CLIENT_ID or VITE_COMMIT_SHA in any image
this Dockerfile has produced.
It went unnoticed because app/routes/*.tsx fall back to
https://api.t4t-gateway.com, which is correct for the VPS image and silently
wrong everywhere else: the homelab instance deployed today served a UI that
called production, exactly what building it separately was meant to prevent.
Verified from the served bundle -- assets/signup-*.js carried the VPS URL.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYFnHida4AdiNDek2pzgXx
Second build failed the same way: docker login to this instance's registry
returns "unauthorized" for the automatic Actions token, and declaring
permissions: packages: write did not change it -- Gitea's task token is accepted
by the API but not by the package registry.
So the push uses REGISTRY_TOKEN, an org secret holding a personal access token
scoped to write:package and nothing else. The step fails with that sentence in
the log rather than another bare "unauthorized", because the difference
between a missing secret and a rejected credential is not visible from the
error docker prints.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYFnHida4AdiNDek2pzgXx
The first build pushed nothing: `docker login git.ffaerber.duckdns.org` came
back "unauthorized". Gitea scopes the automatic Actions token from the job's
permissions block, and without one it has no package write -- so the build
succeeded and the push failed on auth, which reads like a credential problem
rather than a missing declaration.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYFnHida4AdiNDek2pzgXx
Same move as the gateway repo -- build on the homelab runner, push to this
Gitea's registry, no deploy step -- with one difference that matters more here
than anywhere else.
Vite inlines import.meta.env at BUILD time. VITE_API_URL is therefore baked into
the bundle, and the GitHub build bakes https://api.t4t-gateway.com. Running that
image on the homelab would put a UI at t4tgw.ffaerber.duckdns.org that talks to
the VPS: a test signup would create a real account on production, and the two
instances would look identical while being the same instance. This build bakes
the homelab API, which is what makes running them side by side mean anything.
The app also falls back to the production URL when the variable is missing
(app/routes/*.tsx), so an unset build arg fails the same way silently. It is set
explicitly in the workflow env rather than left to a default.
.github/workflows is removed from this copy so Gitea does not try to run the
GitHub one; the GitHub repository keeps its own.
Chat messages previously lived only in React state and vanished on
navigation or refresh. Persist each conversation as a session in
localStorage and add a sidebar to switch between, restore, or delete
past chats, matching the pattern of the account page's client-side
persistence.
Claude-Session: https://claude.ai/code/session_012RrMZ8Y9BhuMkKNcw2bakZ
Co-authored-by: Claude <noreply@anthropic.com>
Three-link nav (Chat / Account / Bee) with active-route highlighting,
plus a t4t Gateway home link on the left. Replaces the bespoke header
on the chat page; account and bee gain the same top-of-page nav.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fetch /v1/jobs alongside the existing account data and render the
last 25 in a table — model, status pill, provider, tokens, cost,
timestamp. Mirrors the homelab gateway's /wallet job list.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Uses the user's JWT against /v1/models + /v1/chat/completions (the
gateway's /v1 routes already accept JWT — no API key needed for the
in-app chat path; API keys remain for external SDK usage).
Single-turn UX for now, no streaming. Model picker reads /v1/models.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
POSTs email + password to /auth/email/login, stores JWT, redirects
to /account (route to be added).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GITHUB_TOKEN defaults to read-only; without packages:write the push
to ghcr.io/token-for-token/hosted-gateway-frontend gets "installation
not allowed to Create organization package".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>