Which GitHub App clones a repo IS visible via the API — resolve source_id instead of the only-App heuristic #74

Closed
opened 2026-07-16 12:17:03 +00:00 by dan-claude-bot · 2 comments
dan-claude-bot commented 2026-07-16 12:17:03 +00:00 (Migrated from github.com)

Summary

semantics.md claims "nothing Coolify returns about an application says which App cloned it", so cast binds every repo to the instance's only GitHub App when there is exactly one and writes a REVIEW-… marker when there is not. The #72 audit refuted the premise at tag v4.1.2:

  • removeSensitiveData on applications does not hide source_id / source_type — both are returned by GET /applications and GET /applications/{uuid} (real columns, Application.php fillable).
  • GET /github-apps exists at v4.1.2 (routes/api.php:131-136) and returns each App's id (and uuid; only client_secret/webhook_secret are hidden, GithubController.php:18-23).

So the association is resolvable via API: application.source_idgithub-apps[].id, guarded by source_type being the GithubApp morph class. The only-App heuristic and the REVIEW marker are unnecessary whenever the lookup succeeds.

What to do

  • inventory / --emit-draft: resolve each application's GitHub App by joining source_id against GET /github-apps; emit the real binding instead of the only-App guess
  • Keep the REVIEW marker only for the genuinely unresolvable case (source_type not a GithubApp, or the id missing from the list — e.g. another team's non-system-wide App)
  • Correct the semantics.md github_apps paragraph and the draft.ts NO_API_COVERAGE row ("which GitHub App clones a repo"), #51-style
  • While in there: POST /github-apps + POST /security/keys exist at 4.1.2 — note in semantics.md that registration of an already-created App is API-doable (the GitHub-side Manifest flow stays manual; that's #7's territory, not this issue's)

Caveats

  • Because GET /github-apps includes other teams' system-wide Apps, resolving by id is still not a proxy for being in the right team — the existing team assert stays.
  • Upstream bugs to be aware of, not blockers for the read path: coollabsio/coolify#10936 (PATCH rejects CUID2 private_key_uuid), #5467 (create times out with many repos).

Found in #72 (finding 8).

## Summary `semantics.md` claims "nothing Coolify returns about an application says which App cloned it", so cast binds every repo to the instance's only GitHub App when there is exactly one and writes a `REVIEW-…` marker when there is not. The #72 audit refuted the premise at tag `v4.1.2`: - `removeSensitiveData` on applications does **not** hide `source_id` / `source_type` — both are returned by `GET /applications` and `GET /applications/{uuid}` (real columns, `Application.php` fillable). - `GET /github-apps` exists at v4.1.2 (`routes/api.php:131-136`) and returns each App's `id` (and `uuid`; only `client_secret`/`webhook_secret` are hidden, `GithubController.php:18-23`). So the association is resolvable via API: `application.source_id` ↔ `github-apps[].id`, guarded by `source_type` being the GithubApp morph class. The only-App heuristic and the REVIEW marker are unnecessary whenever the lookup succeeds. ## What to do - [ ] `inventory` / `--emit-draft`: resolve each application's GitHub App by joining `source_id` against `GET /github-apps`; emit the real binding instead of the only-App guess - [ ] Keep the REVIEW marker only for the genuinely unresolvable case (source_type not a GithubApp, or the id missing from the list — e.g. another team's non-system-wide App) - [ ] Correct the `semantics.md` `github_apps` paragraph and the `draft.ts` `NO_API_COVERAGE` row ("which GitHub App clones a repo"), #51-style - [ ] While in there: `POST /github-apps` + `POST /security/keys` exist at 4.1.2 — note in `semantics.md` that *registration* of an already-created App is API-doable (the GitHub-side Manifest flow stays manual; that's #7's territory, not this issue's) ## Caveats - Because `GET /github-apps` includes other teams' system-wide Apps, resolving by id is still not a proxy for being in the right team — the existing team assert stays. - Upstream bugs to be aware of, not blockers for the read path: coollabsio/coolify#10936 (PATCH rejects CUID2 `private_key_uuid`), #5467 (create times out with many repos). Found in #72 (finding 8).
dan-claude-bot commented 2026-07-16 15:53:12 +00:00 (Migrated from github.com)

Done in #82 (merged, 2cbcfd2) — first three boxes. The fourth is not done; see below.

  • Resolve by joining source_id against GET /github-appsgithubAppNameForProject matches each application's source_id to an App's id, guarded by source_type being the GithubApp morph. The only-App guess is gone.
  • REVIEW marker only for the genuinely unresolvable case — a non-GithubApp source_type (public repo), an id missing from the list, or an instance that will not list its Apps. Also guarded: a source_id that collides with an App id but carries a non-GithubApp source_type is not mistaken for one.
  • Corrected semantics.md's github_apps paragraph + the draft.ts NO_API_COVERAGE row (#51-style), plus the emitted environments.yaml header.
  • "While in there": note in semantics.md that registration of an already-created App is API-doable (POST /github-apps + POST /security/keys). Not done — say the word and I'll send a follow-up; it's a few lines and adjacent to #5/#7.

Worth recording: the old heuristic wasn't merely unnecessary, it was silently wrong on a single-App instance — a public repo (no GitHub App at all) got bound to the one App anyway. The draft-cli fixture now pins exactly that: the incubator app (source_id: 7) resolves to hdb-coolify, while the third-party public repo gets a REVIEW marker. Unit tests cover the multi-App case the heuristic could never handle (it wrote REVIEW on every repo; the lookup resolves each).

Done in #82 (merged, `2cbcfd2`) — first three boxes. The fourth is **not** done; see below. - [x] **Resolve by joining `source_id` against `GET /github-apps`** — `githubAppNameForProject` matches each application's `source_id` to an App's `id`, guarded by `source_type` being the GithubApp morph. The only-App guess is gone. - [x] **REVIEW marker only for the genuinely unresolvable case** — a non-GithubApp `source_type` (public repo), an id missing from the list, or an instance that will not list its Apps. Also guarded: a `source_id` that *collides* with an App id but carries a non-GithubApp `source_type` is not mistaken for one. - [x] **Corrected `semantics.md`'s `github_apps` paragraph + the `draft.ts` `NO_API_COVERAGE` row** (#51-style), plus the emitted `environments.yaml` header. - [ ] **"While in there": note in `semantics.md` that *registration* of an already-created App is API-doable (`POST /github-apps` + `POST /security/keys`).** Not done — say the word and I'll send a follow-up; it's a few lines and adjacent to #5/#7. Worth recording: the old heuristic wasn't merely *unnecessary*, it was **silently wrong** on a single-App instance — a public repo (no GitHub App at all) got bound to the one App anyway. The `draft-cli` fixture now pins exactly that: the incubator app (`source_id: 7`) resolves to `hdb-coolify`, while the third-party public repo gets a REVIEW marker. Unit tests cover the multi-App case the heuristic could never handle (it wrote REVIEW on every repo; the lookup resolves each).
dan-claude-bot commented 2026-07-16 15:57:41 +00:00 (Migrated from github.com)

The unticked fourth box (note that App registration is API-doable at 4.1.2) is now tracked in #84, so it isn't lost with this issue closed.

The unticked fourth box (note that App *registration* is API-doable at 4.1.2) is now tracked in #84, so it isn't lost with this issue closed.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/cast#74
No description provided.