One server can check access against more than one forge: GitHub and a self-hosted GitLab, or two Gitea instances. Objects stay content-addressed in one store, so an asset pushed to a repository on each forge is stored once.
The forge set by LFSX_AUTH stays where it is, at /{org}/{repo}. Every other forge gets a name and
is served under /-/{name}/{org}/{repo}:
LFSX_FORGES=work,home
LFSX_FORGE_WORK_AUTH=gitlab
LFSX_FORGE_WORK_API_URL=https://gitlab.work.example/api/v4
LFSX_FORGE_WORK_ALLOWED=platform/*
LFSX_FORGE_HOME_AUTH=forgejo
LFSX_FORGE_HOME_API_URL=https://git.home.example/api/v1
LFSX_FORGE_HOME_ALLOWED=me/*
A repository on work then uses:
# .lfsconfig
[lfs]
url = https://lfs.example.com/-/work/platform/game-assets
The CLI takes the forge as part of its base URL: lfsx --url https://lfs.example.com/-/work gc --repo platform/game-assets.
Variables
LFSX_FORGES lists the names, comma-separated. A name is 1 to 32 lowercase letters, digits or
dashes, and cannot be api or dashboard, which the dashboard uses. Each name reads its own
variables, the name upper-cased with dashes as underscores (self-hosted reads
LFSX_FORGE_SELF_HOSTED_*):
| Variable | Default | Purpose |
|---|---|---|
LFSX_FORGE_<NAME>_AUTH |
none, required | github, gitlab, gitea or forgejo |
LFSX_FORGE_<NAME>_API_URL |
github.com or gitlab.com, required for Gitea | API root of that forge |
LFSX_FORGE_<NAME>_ALLOWED |
none | repositories served on that forge, as LFSX_ALLOWED |
LFSX_FORGE_<NAME>_RESTRICTED |
none | as LFSX_RESTRICTED, for that forge |
LFSX_FORGE_<NAME>_ANONYMOUS_READ |
false |
as LFSX_ANONYMOUS_READ, for that forge |
The cache lifetimes and the lookup budget (LFSX_AUTH_CACHE_TTL, LFSX_AUTH_REJECTION_TTL,
LFSX_AUTH_LOOKUP_BUDGET) apply to every forge, and each forge spends its own budget. A GitHub App
identity is only available to the forge set by LFSX_AUTH. LFSX_FORGES needs authentication
on: with LFSX_AUTH=disabled the server refuses to start.
What stays apart
acme/game on work and acme/game on the main forge are two repositories. Each has its own
permissions, asked of its own forge, its own allow-list, quota, locks and statistics, and collection
on one never treats the other as a holder it can ignore. On the volume and in the bucket a named
forge's repositories sit under {name}~{org}, for example work~acme/game. ~ cannot appear in an
organisation name, so the main forge's layout is unchanged and nothing moves when a forge is added.
Renaming a forge in LFSX_FORGES therefore points it at a different directory: its repositories
read as empty until the directories are renamed to match.
Dashboard
The dashboard lists every forge under Settings. Its admins are checked against the main forge, and the access settings it edits are the main forge's; the other forges take theirs from the environment.