2.3 KiB
| type | subtype | title | summary | tags | scope | last_updated | date | last_reviewed | related | source | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| reference | api-integration | Forgejo account visibility gates anonymous repo access — and the self-service API can't change it | Why a public Forgejo repo can still 404 anonymously (owner account visibility "limited" hides all its repos), why PATCH /api/v1/user/settings silently ignores the visibility field on Forgejo 1.21, and why raw-file fetch checks should use GET not HEAD. |
|
global | 2026-07-15 | 2026-07-15 | 2026-07-15 |
|
llf-schema |
Forgejo account visibility gates anonymous repo access
Observed on Forgejo 1.21.11 (forgejo.swansoncloud.com), 2026-07-15, while publishing a
public releases repo owned by an account whose visibility was limited.
The gotcha
A repo with private: false is not necessarily anonymously reachable. If the owning
account's user-level visibility is limited, ALL of that account's repos are hidden
from unauthenticated visitors — anonymous requests to the repo and its
/raw/branch/... URLs return 404, even though the repo itself is marked public and
authenticated fetches work fine. The 404 (not 403) makes it look like a wrong URL rather
than an access problem.
The API trap
The self-service endpoint PATCH /api/v1/user/settings returns 200 but silently
ignores a visibility field — the UserSettingsOptions schema (check the instance's
own swagger) has no such property, so the server drops it without error. Account
visibility can only be changed via:
- the web UI: Settings → Account → Visibility, or
- the admin endpoint
PATCH /api/v1/admin/users/<name>with{"visibility": "public"}(requires a token withwrite:adminscope — being an admin user is not enough; the token itself must carry the scope).
Flipping an account limited → public only exposes repos already marked public;
private: true repos stay hidden either way.
Raw-file fetch checks: use GET, not HEAD
The same instance rejects HEAD on some raw-file routes with 405. For "is this zip
URL fetchable?" verification (e.g., before wp plugin install <url>), use
curl -s -o /dev/null -w '%{http_code}' (GET) — it is also what the real consumer does.