Retrieve Actions logs on private repos (token-accepting path or cookie/CSRF auth) #103
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Make
fj run view <n> --log/--log-failedactually retrieve step logs on private repos. Today the only working log path is the Forgejo web frontend route under/{owner}/{repo}/actions/runs/{run}/jobs/{job}/attempt/{attempt}(see the module header insrc/api/workflow_view.rs), which authenticates with a session cookie + CSRF token, not an API token. On a private repo that route 404s under token auth, so logs are unreadable throughfj.Find a path that works under the credentials
fjactually holds (an API token in the OS keychain). Options, roughly in order of preference:/api/v1Actions log endpoint, if the target Forgejo version exposes one. rasterstate/fj#91 reports that on the observed Forgejo (11.0.x / gitea-1.22 era)/api/v1/.../actions/runs/{n}/jobs,/api/v1/.../actions/jobs/{id}/logs, and/api/v1/.../actions/runs/{n}/logsall 404, so this may simply not exist there. Re-verify per version before building on it.Client::request/is_trusted_urlcasually; design it deliberately./actions/tasksfor pass/fail, web UI for logs).Why
fj run view --logis the primary way to read CI output without leaving the terminal, and most of our repos are private (rasterstate/fjord-ios, rasterstate/flux, ...). rasterstate/fj#91 and its duplicate rasterstate/fj#92 both hit this during real CI triage and had to fall back to the/actions/tasksAPI for pass/fail and to server-side sqlite (action_task_step.log_length) for ground truth. The error-message fix in rasterstate/fj#91 stops the misdiagnosis, but it does not restore the feature: private-repo logs still cannot be read throughfj.Acceptance
fj run view <n> --logand--log-failedprint real step logs for a private repo run that has logs, against a Forgejo version where a supported path exists.Client::requestand respectsis_trusted_url(no second, unguarded HTTP/credential path).cargo fmt --check,cargo clippy --all-targets --all-features -- -D warnings,cargo test --allpass.Dependencies
Out of scope
Size
L (uncertain; may reduce to "document the limitation" if no auth path exists)
Product-validation + feasibility: private-repo Actions log retrieval
Posting this as the operator-facing decision record for this parked item. Two parts: (1) is the pain worth the work, and (2) if so, by which path. TL;DR up front: the acute pain is already cured by rasterstate/fj#91, the remaining pain is a recurring papercut, and the only client-side path (cookie/CSRF) costs far more than the papercut. Pursue a server-side/gateway endpoint and an upstream request instead; keep this parked behind the shipped documented-limitation.
1. Product validation
Who needs it, and how often. This is a real, recurring need, not a hypothetical.
fj run watch/fj run view --logis the headline reason to usefjover the web UI at all: read CI output without leaving the terminal. Most of our repos are private (rasterstate/fjord-ios, rasterstate/flux, and the rest), so "private-repo logs" is not an edge case, it is the common case for anyone triaging CI here. Frequency tracks how often private-repo CI goes red and someone wants the log in-terminal, which for active repos is daily-ish. So on raw frequency this scores high.The cost of today's fallback. After rasterstate/fj#91 shipped, the fallback is three layers, and they are better than they sound:
no logs for that run/job/attempt, which reads as "the job died before producing logs." rasterstate/fj#92 documents that exact error sending a triage on a multi-step detour through runner provisioning and autoscaler limits for what was an ordinary step failure whose logs existed the whole time. rasterstate/fj#91 replaced that with a message that names the real cause (web log route rejects token auth; private-repo logs need session auth) and points here. That converts a wrong answer into a correct "go look in the web UI" signpost. The expensive part of the pain (chasing a nonexistent cause) is already gone./actions/taskslist. Still readable under the PAT, sofj run listand pass/fail status work in-terminal on private repos. You lose the log body, not run state.Does the pain justify the work? Honest read: the remaining pain is a recurring papercut, not a fire, and it does not justify an expensive fix. Two of our own issues (rasterstate/fj#91, rasterstate/fj#92) were filed from real CI triage that this blocked, which is genuine evidence the feature matters, but both predate the rasterstate/fj#91 error fix. The thing that actually hurt (misdiagnosis) is cured. What's left is "on a private repo,
--logcan't print the body, click through to the web UI instead," correctly signposted. That justifies a cheap fix (an upstream ask, or a server-side endpoint we get nearly for free) and the already-shipped documentation. It does not justify forking the auth model to chase it. The work is only worth doing if the path is cheap.2. Feasibility
Technical baseline (re-confirmed against the code).
src/api/workflow_view.rsis explicit that/api/v1exposes no Actions log surface: single-run, per-run/jobs,/jobs/{id}/logs, and/runs/{id}/logsall 404. Both the run summary and the logs live only behind the human web routes under/{owner}/{repo}/actions/.... On a public repo those routes answer a token-bearing request; on a private repo they 404 because they want a session cookie + CSRF token, not a PAT. So the only client-side path to private logs is to authenticate as the browser does.What browser-session auth concretely requires. This is a second, heavier auth system, not a tweak:
fj auth logintoday prompts for a PAT and stores one opaque string per host (src/auth/mod.rs). Cookie auth means prompting for a password, a regression in credential hygiene (PATs are scoped and revocable; a password is not)._csrftoken from a form/meta tag or cookie and replay it on every state-changing web request. Brittle, tied to the HTML, unversioned.reqwestis currently used statelessly through oneClient::request; we'd add cookie-store handling and a parallel notion of "logged-in session" alongside the token.src/auth/mod.rsdeliberately stores only tokens, never to disk).Security + maintenance burden. High and permanent. We'd be storing a second class of credential, prompting for passwords, parsing CSRF out of HTML, and depending on unversioned web routes plus a login form that upstream can restyle or re-auth at any release, with no API contract. The module header already flags these routes as "stable in practice but can shift between Forgejo releases." Every one of those shifts becomes a
fj-breaks-on-upgrade incident. For a papercut-sized payoff, this is a bad trade.Alternatives, ranked.
/api/v1/.../actions/jobs/{id}/logs(or equivalent). If it lands,fjdeletes the whole web-route hack and private logs become an ordinary typed/api/v1wrapper, the cleanest possible end state. Cost to us is a writeup; risk is timeline (not ours to control). Low effort, high upside, no downside, so it's worth filing regardless of what else we do.src/fjord/mod.rsshows Fjord-Account traffic routing through fjord-platform's/api/v1/forge-gateway/:instance-id/api/v1/..., and the platform already performs a credentialedsign-in(it even handles a 2FA recovery code). Crucially,workflow_view.rsnotes the gateway only proxies/api/v1, so it doesn't forward the web log routes today, which is exactly the gap. The server side can hold the session (or readaction_task_step.log_length/ the log blobs directly, the same ground truth rasterstate/fj#91 and rasterstate/fj#92 had to reach via sqlite) and expose a clean token-or-bearer log endpoint thatfjcalls like any other gateway route. This keeps all the session/CSRF/2FA/expiry burden server-side where it's already solved, instead of shipping it to every client. Effort is real but bounded, and it only helps Fjord-Account users (PAT-direct users still need option 1 or the web UI). This is where to invest if the papercut proves worth investing in./actions/tasksstill gives in-terminal pass/fail. Add a troubleshooting note ("private-repo log bodies aren't retrievable via PAT; use the web UI or a Fjord-Account instance") and this is a defensible resting state indefinitely.Recommendation
Do not build client-side cookie/CSRF auth. It forks the auth model (passwords, cookie jar, CSRF scraping, 2FA, expiry, second keychain secret) against unversioned web routes, a permanent maintenance and security tax, to fix a papercut that rasterstate/fj#91 already de-fanged.
Instead:
No production code written and no labels changed here; leaving the parked/backlog state for you to set.