Promoting a skill or an agent type opens a pull request against a registry repository. Almost none of what that flow needs is configurable: one value is, one is derived at runtime, and the rest are hardcoded constants. This is a problem for anyone whose setup is not the default one — a self-hosted GitHub Enterprise, an organisation whose fork does not live under the token owner's account, a repository whose default branch is not the one the PR should target, or a deployment that needs commits attributed to a service identity rather than to whoever's token happens to be configured.
What each promotion path needs, and where it comes from today
Both HTTP paths share one engine, crates/librefang-skills/src/registry_pr.rs (fork → branch → put files → open PR), used by propose_skill_to_registry (registry_pr.rs:100) and propose_files_to_registry (registry_pr.rs:170).
| Value |
Source today |
Location |
| GitHub API base |
hardcoded https://api.github.com |
registry_pr.rs:31 |
upstream repo owner/name |
caller-supplied, from skills.registry_repo |
registry_pr.rs:59, :80 |
| default for that repo |
hardcoded librefang/librefang-registry |
registry_pr.rs:28 |
| token |
caller-supplied |
registry_pr.rs:60, :81 |
| owner / login the fork lands under |
derived at runtime from GET /user |
registry_pr.rs:519-524, used at :116, :190 |
| fork repo |
computed {login}/{repo_name(upstream)} |
registry_pr.rs:118, :191 |
| base branch |
queried: the fork's default branch |
registry_pr.rs:122, :194 |
| head branch name |
computed <prefix>/<name>-<timestamp> |
registry_pr.rs:123-127, :195-199 |
| commit author / committer |
not sent at all — put_file posts only message, content, branch and an optional sha, so GitHub attributes every commit to the token's account |
registry_pr.rs:601-627 |
| fork vs direct push |
no such choice — the flow always forks |
— |
RegistryConfig::registry_mirror and registry_host (crates/librefang-types/src/config/types.rs:5988, :5993) exist but govern the read side (registry sync). The promotion paths never consult them — grep -n "registry_mirror\|registry_host" crates/librefang-skills/src/registry_pr.rs returns nothing.
The only configurable value is skills.registry_repo (types.rs:1687), which is already reachable through POST /api/config/set and the dashboard's generic ConfigPage.
There is a third path with its own separate problem: the CLI librefang skill publish (crates/librefang-cli/src/commands/skill.rs:675) publishes a GitHub release rather than a PR, reads GITHUB_TOKEN / GH_TOKEN from the environment only — no vault fallback, unlike the HTTP routes — and exits with status 1 when neither is set. Its API base and organisation are hardcoded in MarketplaceConfig::default() (marketplace.rs:117-125), reachable only via the --repo flag.
Proposal
Add a dedicated configuration section covering the values above — API base URL, target owner for the fork, base branch, head-branch prefix, commit author name and email, and an explicit fork-vs-direct-push mode — and have registry_pr.rs read them, keeping every current behaviour as the default so nothing changes for an existing installation. Where a value is derived today (the login from GET /user, the fork's default branch), the derivation stays as the fallback when the setting is unset, rather than being replaced by a required field.
Two of these are correctness issues rather than conveniences:
Commit author. Not sending author/committer means the commit is attributed to whoever owns the token. For a shared or service token that is wrong, and it is not something the operator can currently correct.
API base URL. Hardcoding api.github.com makes the whole promotion feature unusable on GitHub Enterprise, with no workaround.
Alignment worth doing at the same time: skill publish should resolve its token the same way the HTTP routes do (environment, then vault), because the current split means the same daemon can promote a skill through the API and fail to publish one from the CLI on the same machine.
Promoting a skill or an agent type opens a pull request against a registry repository. Almost none of what that flow needs is configurable: one value is, one is derived at runtime, and the rest are hardcoded constants. This is a problem for anyone whose setup is not the default one — a self-hosted GitHub Enterprise, an organisation whose fork does not live under the token owner's account, a repository whose default branch is not the one the PR should target, or a deployment that needs commits attributed to a service identity rather than to whoever's token happens to be configured.
What each promotion path needs, and where it comes from today
Both HTTP paths share one engine,
crates/librefang-skills/src/registry_pr.rs(fork → branch → put files → open PR), used bypropose_skill_to_registry(registry_pr.rs:100) andpropose_files_to_registry(registry_pr.rs:170).https://api.github.comregistry_pr.rs:31owner/nameskills.registry_reporegistry_pr.rs:59,:80librefang/librefang-registryregistry_pr.rs:28registry_pr.rs:60,:81GET /userregistry_pr.rs:519-524, used at:116,:190{login}/{repo_name(upstream)}registry_pr.rs:118,:191registry_pr.rs:122,:194<prefix>/<name>-<timestamp>registry_pr.rs:123-127,:195-199put_fileposts onlymessage,content,branchand an optionalsha, so GitHub attributes every commit to the token's accountregistry_pr.rs:601-627RegistryConfig::registry_mirrorandregistry_host(crates/librefang-types/src/config/types.rs:5988,:5993) exist but govern the read side (registry sync). The promotion paths never consult them —grep -n "registry_mirror\|registry_host" crates/librefang-skills/src/registry_pr.rsreturns nothing.The only configurable value is
skills.registry_repo(types.rs:1687), which is already reachable throughPOST /api/config/setand the dashboard's generic ConfigPage.There is a third path with its own separate problem: the CLI
librefang skill publish(crates/librefang-cli/src/commands/skill.rs:675) publishes a GitHub release rather than a PR, readsGITHUB_TOKEN/GH_TOKENfrom the environment only — no vault fallback, unlike the HTTP routes — and exits with status 1 when neither is set. Its API base and organisation are hardcoded inMarketplaceConfig::default()(marketplace.rs:117-125), reachable only via the--repoflag.Proposal
Add a dedicated configuration section covering the values above — API base URL, target owner for the fork, base branch, head-branch prefix, commit author name and email, and an explicit fork-vs-direct-push mode — and have
registry_pr.rsread them, keeping every current behaviour as the default so nothing changes for an existing installation. Where a value is derived today (the login fromGET /user, the fork's default branch), the derivation stays as the fallback when the setting is unset, rather than being replaced by a required field.Two of these are correctness issues rather than conveniences:
Commit author. Not sending
author/committermeans the commit is attributed to whoever owns the token. For a shared or service token that is wrong, and it is not something the operator can currently correct.API base URL. Hardcoding
api.github.commakes the whole promotion feature unusable on GitHub Enterprise, with no workaround.Alignment worth doing at the same time:
skill publishshould resolve its token the same way the HTTP routes do (environment, then vault), because the current split means the same daemon can promote a skill through the API and fail to publish one from the CLI on the same machine.