Skip to content

feat(controllers): read a private catalog with an App installation token - #250

Merged
gusfcarvalho merged 3 commits into
mainfrom
fx/cat-a1-private-catalog
Oct 29, 2026
Merged

gusfcarvalho merged 3 commits into
mainfrom
fx/cat-a1-private-catalog

Conversation

@ccf-lisa

@ccf-lisa ccf-lisa Bot commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Task: IMPROVEMENTS item 8, stack A PR A1 of 3: fx/cat-a1-private-catalog on main.

Summary

factory serve can now read a private catalog repository (CCF's prod catalog moves to compliance-framework/factory-catalog) with an installation token of a GitHub App of its own, since the App keys the catalog's Provider declares can't bootstrap reading that catalog.

  • gitsource.LoadAuth (internal/catalog/gitsource): Load with an Auth. The fetch sends the token exactly as HeadAuth does (an Authorization header scoped to the repo URL, in git's process-scoped config; never the URL or argv), and every error is redacted with Auth.secrets. Load calls it with a zero Auth, so its behaviour is unchanged. The URL check HeadAuth had inline is now Auth.check, shared by both.
  • controllers.GitSource.Token: when set, Head and Load mint a token for owner/name first (RepoOnHost) and pass gitsource.Auth{Token: tok}. A mint error (or an empty token) fails the call wrapped, with no anonymous fallback; a Repo that isn't https://<host>/<owner>/<name>[.git] is an error at first use. Head on a pinned commit SHA mints nothing. String() stays credential-free.
  • serve flags: --catalog-app-id N and --catalog-app-key-ref name/key (the PEM from FACTORY_SECRET_<NAME>_<KEY>, as for catalog secretRefs). Validation: the two go together; neither combines with --catalog-dir or --dev-fake-providers; the ref must be name/key; the variable must be set; and --catalog-repo must be https://<git host>/<owner>/<name> on the git host of --github-api-url (default github.com), so the token can't be sent to another host. Errors name flags and variables, never values.
  • Wiring (catalogSource in serve_app.go): one tokens.Minter with the single identity catalog, APIURL = --github-api-url else https://api.github.com, serve's clock, logger and HTTP client; GitSource.Token mints provider.ReadOnlyRequest("catalog", repo, 10m). catalogSource is now built once and returns an error, so a bad PEM fails at startup through tokens.New.
  • Docs: the serve usage text, a short "Private catalog" paragraph in deploy/poc/README.md §4, FACTORY_CATALOG_APP_ID (optional) in run.sh and .env.example (adds both flags with ccf-factory-app/private-key), and the flag row plus validation clause in the serve LLD (api-server.md).

Verification

  • make reviewable: passes (generated-code check, golangci-lint v2.11.4 with the shared config, go test -race -count=1 ./..., catalog checks 66 cases, 66 ok, vacuum 100/100).
  • New tests:
    • TestLoadAuth (gitsource, over the local smart-HTTPS git server auth_test.go already has): a private repo loads with the token and fails anonymously or with a wrong token; a ref named like the token makes git print it in its error, and the test checks it is redacted (it is: couldn't find remote ref redacted); a token on a file:// URL is refused.
    • TestGitSourceToken / TestGitSourcePinnedHead (controllers, with injected headAuth/loadAuth): the minted token reaches both Head and Load; with no Token both are anonymous; a mint error, an empty token, and ssh, http, non-repo and user-info URLs fail without any gitsource call; a pinned commit needs no mint.
    • TestParseServeConfig: 12 new rows for the flag validation above (including GHE and the key value never appearing in an error).
    • TestCatalogSourceToken (cmd/factory, against githubtest): the wired GitSource.Token mints a real (fake-server) installation token for the catalog repo as App 7; a bad PEM fails catalogSource without echoing it.
  • What only a real private repo plus the real App can check (Gustavo's check): that the installed App's contents: read token reads compliance-framework/factory-catalog end to end:
    FACTORY_SECRET_CCF_FACTORY_APP_PRIVATE_KEY=… factory serve --catalog-repo https://github.com/compliance-framework/factory-catalog --catalog-app-id <id> --catalog-app-key-ref ccf-factory-app/private-key …
    
    Expect catalog loaded with the repo's commit as sha; without the App installed on the repo, startup fails with mint a read token for the catalog repository compliance-framework/factory-catalog: ….

Design decisions

  • No anonymous fallback when Token is set (unlike GitSkills.Token): a private catalog read anonymously only adds a second, confusing error; catalog-sync already retries and keeps the last good catalog.
  • The host check in serve goes slightly beyond the spec's "a non-GitHub URL is an error": GitSource checks the URL's form, and serve checks the host is the App's git host, because the token is minted for that host.

For triage

  • Ownership. This PR is dispatcher-runtime's (internal/controllers, cmd/factory serve), plus the Auth parameter in dispatcher-core's internal/catalog/gitsource as the spec allows. It also edits deploy/poc/{README.md,run.sh,.env.example} (poc-rollout's kit; named by the spec) and design/workstreams/dispatcher-runtime/LLD/api-server.md (not named by the spec; added by self-review so the LLD's flag table matches serve).
  • run.sh has no catalog repo setting. deploy/poc/run.sh hard-codes --catalog-repo https://github.com/ContainerSolutions/factory (the catalog=(…) block), so FACTORY_CATALOG_APP_ID alone reads this public repo with a token. To read compliance-framework/factory-catalog today, pass --catalog-repo https://github.com/compliance-framework/factory-catalog as an extra argument to run.sh (extra arguments come last, so they win). Proposed fix: A3, which rewrites §4 and stops the default, adds FACTORY_CATALOG_REPO to run.sh and .env.example.

🤖 Generated with Claude Code

ccf-lisa Bot and others added 3 commits October 29, 2026 08:30
gitsource.LoadAuth fetches with a token as HeadAuth does; controllers.GitSource
mints one per Head/Load through its Token func; factory serve gets
--catalog-app-id and --catalog-app-key-ref and builds a one-identity minter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…w pass 1)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@gusfcarvalho gusfcarvalho left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ccf-review: COMMENT

1 Should-fix.

Comment thread deploy/poc/run.sh Resolved
gusfcarvalho added a commit that referenced this pull request Oct 29, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gusfcarvalho
gusfcarvalho added this pull request to stack #253 October 29, 2026 12:22
@gusfcarvalho
gusfcarvalho merged commit fb1fa26 into main Oct 29, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant