Skip to content

Fix ConvertToVersion to prefer object's GVK field over first registered kind - #135537

Open
kvaps wants to merge 2 commits into
kubernetes:masterfrom
kvaps:fix-ConvertToVersion
Open

kvaps wants to merge 2 commits into
kubernetes:masterfrom
kvaps:fix-ConvertToVersion

Conversation

@kvaps

@kvaps kvaps commented Dec 1, 2025 •

Copy link
Copy Markdown
Member

What type of PR is this?

/kind bug

What this PR does / why we need it:

Fixes ConvertToVersion() to prefer the object's GVK field over the first registered kind when multiple kinds are registered with the same Go type.

When multiple kinds are registered with the same Go type using scheme.AddKnownTypeWithName(), ConvertToVersion() was always selecting the first registered kind from typeToGVK instead of using the actual GVK from the object's field. This causes incorrect behavior in dynamic API servers where the same Go type (e.g., Application{}) is registered multiple times with different kinds (e.g., BootBox, Tenant, Postgres) at runtime.

Use case:
In the cozystack.io project, we use a dynamic API-server where types are registered dynamically at runtime using gvk.GroupVersion().WithKind(kind) multiple times for the same Go type Application{}. When ConvertToVersion() is called, it incorrectly returns the first registered type instead of the actual type from the object's field. For more details on how this dynamic API server works, see the blog post.

Workaround limitations:

  • For regular objects, a workaround exists using unstructured.Unstructured{}, which reads the type from the field via a fast path in apimachinery/pkg/runtime/serializer/versioning/versioning.go, avoiding ConvertToVersion().
  • However, this workaround does not work for List types, making this fix necessary.

This PR ensures that when an object has a valid GVK in its field that matches one of the registered kinds, that GVK is used instead of always defaulting to the first registered kind.

Which issue(s) this PR is related to:

N/A

Special notes for your reviewer:

This change modifies the convertToVersion function in staging/src/k8s.io/apimachinery/pkg/runtime/scheme.go to:

  1. First check if the object's GVK field is set and matches one of the registered kinds
  2. If found and acceptable for the target version, use that GVK
  3. Otherwise, fall back to the existing logic of selecting from registered kinds

The fix includes comprehensive tests for both typed objects and typed lists to verify that:

  • Objects with different GVKs in their fields return the correct GVK after conversion
  • The fix works for both regular objects and List types
  • All registered kinds return different GVKs (not all returning the first registered)

Example usage context:
In cozystack.io, types are registered like this:

scheme.AddKnownTypeWithName(gv.WithKind("BootBox"), &Application{})
scheme.AddKnownTypeWithName(gv.WithKind("Tenant"), &Application{})
scheme.AddKnownTypeWithName(gv.WithKind("Postgres"), &Application{})

Before this fix, all three would return the GVK of "BootBox" (first registered) after ConvertToVersion(). After this fix, each object returns its actual GVK from the field.

Does this PR introduce a user-facing change?

Fixed `ConvertToVersion()` to correctly use the object's GVK field when multiple kinds are registered with the same Go type. Previously, it would always return the first registered kind, causing incorrect behavior in dynamic API servers where the same Go type is registered multiple times with different kinds.

Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:

- [Blog post: Dynamic Kubernetes API Server for Cozystack]: https://kubernetes.io/blog/2024/11/21/dynamic-kubernetes-api-server-for-cozystack/
- [Usage example]: https://github.com/cozystack/cozystack/blob/8bc62d4c715f624da930d06404b8048001f8fa1d/pkg/apis/apps/v1alpha1/register.go#L58-L73

AI Disclosure

AI tooling (Claude) was used to assist in preparing this PR. All changes have been reviewed and verified by the author. This fix has been running in production (cozystack) for a long time from a forked apimachinery.

@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. do-not-merge/needs-kind Indicates a PR lacks a `kind/foo` label and requires one. do-not-merge/needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. labels Dec 1, 2025
@k8s-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: kvaps
Once this PR has been reviewed and has the lgtm label, please assign smarterclayton for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. and removed do-not-merge/needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. labels Dec 1, 2025
@k8s-ci-robot k8s-ci-robot added release-note-none Denotes a PR that doesn't merit a release note. release-note Denotes a PR that will be considered when it comes time to generate release notes. and removed do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. release-note-none Denotes a PR that doesn't merit a release note. labels Dec 1, 2025
@kvaps
kvaps force-pushed the fix-ConvertToVersion branch from 746113f to be3af7c Compare December 1, 2025 19:45
kvaps added a commit to cozystack/cozystack that referenced this pull request Dec 1, 2025
…st GVK

This commit refactors the apiserver REST handlers to use typed objects
(appsv1alpha1.Application) instead of unstructured.Unstructured, eliminating
the need for runtime conversions and simplifying the codebase.

Additionally, it fixes an issue where UnstructuredList objects were using
the first registered kind from typeToGVK instead of the kind from the
object's field when multiple kinds are registered with the same Go type.

This is a more comprehensive fix for the problem addressed in
#1630, which was reverted in
#1677.

The fix includes the upstream fix from kubernetes/kubernetes#135537,
which enables short-circuit path for UnstructuredList similar to regular
Unstructured objects, using GVK from the object field instead of
typeToGVK.

Changes:
- Refactored rest.go handlers to use typed Application objects
- Removed unstructured.Unstructured conversions
- Fixed UnstructuredList GVK handling
- Updated dependencies in go.mod/go.sum
- Added e2e test for OpenAPI validation
- Updated Dockerfile

Signed-off-by: Andrei Kvapil <[email protected]>
kvaps added a commit to cozystack/cozystack that referenced this pull request Dec 1, 2025
…st GVK

This commit refactors the apiserver REST handlers to use typed objects
(appsv1alpha1.Application) instead of unstructured.Unstructured, eliminating
the need for runtime conversions and simplifying the codebase.

Additionally, it fixes an issue where UnstructuredList objects were using
the first registered kind from typeToGVK instead of the kind from the
object's field when multiple kinds are registered with the same Go type.

This is a more comprehensive fix for the problem addressed in
#1630, which was reverted in
#1677.

The fix includes the upstream fix from kubernetes/kubernetes#135537,
which enables short-circuit path for UnstructuredList similar to regular
Unstructured objects, using GVK from the object field instead of
typeToGVK.

Changes:
- Refactored rest.go handlers to use typed Application objects
- Removed unstructured.Unstructured conversions
- Fixed UnstructuredList GVK handling
- Updated dependencies in go.mod/go.sum
- Added e2e test for OpenAPI validation
- Updated Dockerfile

Signed-off-by: Andrei Kvapil <[email protected]>
@kvaps
kvaps force-pushed the fix-ConvertToVersion branch from be3af7c to 88775b8 Compare December 1, 2025 20:43
kvaps added a commit to cozystack/cozystack that referenced this pull request Dec 1, 2025
…st GVK

This commit refactors the apiserver REST handlers to use typed objects
(appsv1alpha1.Application) instead of unstructured.Unstructured, eliminating
the need for runtime conversions and simplifying the codebase.

Additionally, it fixes an issue where UnstructuredList objects were using
the first registered kind from typeToGVK instead of the kind from the
object's field when multiple kinds are registered with the same Go type.

This is a more comprehensive fix for the problem addressed in
#1630, which was reverted in
#1677.

The fix includes the upstream fix from kubernetes/kubernetes#135537,
which enables short-circuit path for UnstructuredList similar to regular
Unstructured objects, using GVK from the object field instead of
typeToGVK.

Changes:
- Refactored rest.go handlers to use typed Application objects
- Removed unstructured.Unstructured conversions
- Fixed UnstructuredList GVK handling
- Updated dependencies in go.mod/go.sum
- Added e2e test for OpenAPI validation
- Updated Dockerfile

Signed-off-by: Andrei Kvapil <[email protected]>
kvaps added a commit to cozystack/cozystack that referenced this pull request Dec 1, 2025
…st GVK

This commit refactors the apiserver REST handlers to use typed objects
(appsv1alpha1.Application) instead of unstructured.Unstructured, eliminating
the need for runtime conversions and simplifying the codebase.

Additionally, it fixes an issue where UnstructuredList objects were using
the first registered kind from typeToGVK instead of the kind from the
object's field when multiple kinds are registered with the same Go type.

This is a more comprehensive fix for the problem addressed in
#1630, which was reverted in
#1677.

The fix includes the upstream fix from kubernetes/kubernetes#135537,
which enables short-circuit path for UnstructuredList similar to regular
Unstructured objects, using GVK from the object field instead of
typeToGVK.

Changes:
- Refactored rest.go handlers to use typed Application objects
- Removed unstructured.Unstructured conversions
- Fixed UnstructuredList GVK handling
- Updated dependencies in go.mod/go.sum
- Added e2e test for OpenAPI validation
- Updated Dockerfile

Signed-off-by: Andrei Kvapil <[email protected]>
@kvaps

kvaps commented Dec 1, 2025

Copy link
Copy Markdown
Member Author

/test pull-kubernetes-e2e-gce

@siyuanfoundation

Copy link
Copy Markdown
Contributor

/cc @siyuanfoundation

/triage accepted

@k8s-ci-robot k8s-ci-robot added triage/accepted Indicates an issue or PR is ready to be actively worked on. and removed needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. labels Dec 4, 2025
@siyuanfoundation

Copy link
Copy Markdown
Contributor

/cc @liggitt

@k8s-ci-robot
k8s-ci-robot requested a review from liggitt December 9, 2025 14:35
…ed kind

When multiple kinds are registered with the same Go type using
AddKnownTypeWithName, ConvertToVersion was always selecting the first
registered kind from typeToGVK instead of using the actual GVK from
the object's field. This causes incorrect behavior in dynamic API
servers where the same Go type is registered multiple times with
different kinds.

Reorder the kinds slice so the object's group/kind is preferred while
still passing the full list to KindForGroupVersionKinds for priority
decisions. Fall back to the original order when the chosen GVK is
not in the registered list (e.g., InternalGroupVersioner constructs
a fallback from the first element).

Signed-off-by: Andrei Kvapil <[email protected]>
@kvaps
kvaps force-pushed the fix-ConvertToVersion branch from 29a8fe5 to cad151d Compare May 5, 2026 21:34
@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. and removed cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. labels May 5, 2026
@kvaps

kvaps commented May 5, 2026

Copy link
Copy Markdown
Member Author

Hey @dims

Done, I added an AI disclosure to the PR description.
Thanks for the link to the guidance. We'll look into adopting a similar practice in Cozystack project.

@kvaps

kvaps commented May 6, 2026

Copy link
Copy Markdown
Member Author

/retest

1 similar comment
@kvaps

kvaps commented May 7, 2026

Copy link
Copy Markdown
Member Author

/retest

@kvaps
kvaps requested a review from liggitt June 4, 2026 14:22
@kvaps

kvaps commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

Hi @liggitt, friendly nudge on this one. Should I join an upcoming SIG API Machinery meeting to walk through this fix, or is there another way that would help move it forward? Thanks!

@neolit123 neolit123 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

it might regress some users who were relying on the existing behavior for the same go structs. did you grep to check if we have some of that in k/k?

performance wise it can be made close to no impact on existing setups that don't do it, so that's at least nice.

Comment on lines +528 to +530
var exactMatch []schema.GroupVersionKind
var groupKindMatch []schema.GroupVersionKind
var rest []schema.GroupVersionKind

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
var exactMatch []schema.GroupVersionKind
var groupKindMatch []schema.GroupVersionKind
var rest []schema.GroupVersionKind
var exactMatch, groupKindMatch, rest []schema.GroupVersionKind

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Rewrote this block, see below

// constructed a fallback GVK from the first element (e.g., InternalGroupVersioner).
// Reordering can change which element is first, producing an incorrect GVK.
// Retry with the original order to get the correct fallback.
if &kinds[0] != &originalKinds[0] {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

much cleaner if there is reordered bool var.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done

Comment on lines +525 to +526
objGVK := in.GetObjectKind().GroupVersionKind()
if len(objGVK.Kind) > 0 && len(objGVK.Version) > 0 && len(kinds) > 1 {

@neolit123 neolit123 Aug 5, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

maybe:

Suggested change
objGVK := in.GetObjectKind().GroupVersionKind()
if len(objGVK.Kind) > 0 && len(objGVK.Version) > 0 && len(kinds) > 1 {
if len(kinds) > 1 {
objGVK := in.GetObjectKind().GroupVersionKind()
if len(objGVK.Version) > 0 && len(objGVK.Kind) > 0 {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done

Comment on lines +528 to +546
var exactMatch []schema.GroupVersionKind
var groupKindMatch []schema.GroupVersionKind
var rest []schema.GroupVersionKind
for _, k := range kinds {
switch {
case k == objGVK:
exactMatch = append(exactMatch, k)
case k.Group == objGVK.Group && k.Kind == objGVK.Kind:
groupKindMatch = append(groupKindMatch, k)
default:
rest = append(rest, k)
}
}
if len(exactMatch) > 0 || len(groupKindMatch) > 0 {
reordered = append(reordered, exactMatch...)
reordered = append(reordered, groupKindMatch...)
reordered = append(reordered, rest...)
kinds = reordered
}

@neolit123 neolit123 Aug 5, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

...but this does do a number of allocations.

instead there could be a single allocation:

reordered := make([]schema.GroupVersionKind, 0, len(kinds))

then a copy() and e..g. a slices.SortStableFunc that compares each slice A and B elements against the object GVs Group/Kind and determines the A / B element order.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done, slices.Clone + SortStableFunc. Stable sort keeps registration order within buckets

@benluddy

benluddy commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

/cc

@kubernetes-prow
kubernetes-prow Bot requested a review from benluddy August 5, 2026 18:08
@fedebongio

Copy link
Copy Markdown
Contributor

/assign @benluddy

@fedebongio

Copy link
Copy Markdown
Contributor

We discussed this issue, in the Aug 5th SIG Meeting, and there was a general agreement that needs to be fixed and that is trending in the right direction.

  • [liggitt] mentioned that he did a pass in the past, and would appreciate an extra couple of eyes looking at the changes.
  • [benluddy] kindly volunteered to take an additional pass

@benluddy

benluddy commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

[benluddy] kindly volunteered to take an additional pass

I'm on it, just taking some time to better understand other API implications of registering multiple kinds per type. I'll sync with @liggitt and follow up next week.

Address review feedback: hoist the cheap len(kinds) check before reading
the object's GVK, replace the three bucket slices with a single cloned
slice ordered by a stable sort, and track the reorder with a boolean
instead of comparing slice element addresses.

Signed-off-by: Andrei Kvapil <[email protected]>
@kubernetes-prow

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: kvaps
Once this PR has been reviewed and has the lgtm label, please assign smarterclayton for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kvaps

kvaps commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

Yes, grepped. The only non-test multi-kind registration in k/k is metav1.WatchEvent, and its GetObjectKind() returns EmptyObjectKind, so the reorder never triggers for it. The rest are fake/test schemes (dynamic/fake, metadata/fake, kubectl testing), their suites pass unchanged. Typed in-tree objects carry empty TypeMeta, so nothing changes for them either.

@benluddy

Copy link
Copy Markdown
Contributor

The endpoint handlers appear to have the correct (singular) GVK on the request scope, but they discard the kind just before serializing response objects:

responsewriters.WriteObjectNegotiated(serializer, scope, kind.GroupVersion(), w, req, statusCode, obj, false)

negotiatedEncoder = scope.Serializer.EncoderForVersion(serializer.Serializer, contentKind.GroupVersion())

The GVK on the request scope is populated from the REST storage's GroupVersionKindProvider implementation:

// Let the storage tell us exactly what GVK it has
if gvkProvider, ok := storage.(rest.GroupVersionKindProvider); ok {
return gvkProvider.GroupVersionKind(groupVersion), nil
}

A GroupVersionKindProvider implementation seems to be effectively required today, in order to avoid the same class of problem in discovery and OpenAPI stemming from multiple registered GVKs per kind.

If we pass a GroupVersioner implementation that recognizes the expected kind, instead of one that picks the first matching GV, does that address the same use case without changing Scheme's conversion behavior? I agree that it's not good that conversion fails to take the hint when apiVersion and kind are populated on the object itself, but I would like to solve this issue without requiring apiVersion and kind to be populated on every object. From what I've seen, the expectations around those fields are inconsistent across the codebase.

I took a stab at writing prompting tests for the relevant serving behavior and a POC fix to the API server plumbing in #141344. I'd be happy to complete that for 1.38 if it's an equally acceptable solution.

kvaps added a commit to cozystack/cozystack that referenced this pull request Aug 20, 2026
…3784)

## What this PR does

Brings `GOVERNANCE.md` in line with the organisation it describes: what
the project governs, and what it ships but does not.

### Sub-projects

The `Code Repositories` list named seven repositories and was wrong in
both directions. Five of the six sub-projects whose code ships in the
distribution were missing from it — `etcd-operator`,
`keycloak-kms-proxy`, `local-ccm`, `cozystack-scheduler` and
`ingress-nginx-with-protobuf-exporter` — while `talos-bootstrap` and
`talos-meta-tool` were listed although an adopter installs neither.
Nothing stated what being on the list meant, which is why it drifted.

It is replaced by a section organised around the distinction that
decides scope: whether a repository's output reaches the artifact an
adopter installs. Packaged sub-projects get a table naming what each one
contributes, its stability, and its owners — the same handles each
repository's `CODEOWNERS` already carries. Independent tooling, project
infrastructure and upstream forks are described in a line each, as
explicitly outside that scope.

Three things an evaluator finds anyway are stated rather than left to be
discovered: that one fork (`apimachinery`) is compiled into the product,
why, and that the fix is proposed upstream as
kubernetes/kubernetes#135537; that four repositories are private, with
the reason for each, and that none of them contributes to the
distribution; and that telemetry is received by a component adopters do
not install, with a link to the opt-out.

### Third-party Dependencies

Writes down which shipped components the project does not govern.
Cozystack vendors software it does not own, which is ordinary and needs
no defence — but the boundary was nowhere stated. This is a Required
item on the Incubation checklist ("All project metadata and resources
are vendor-neutral"), and an evaluator currently has nothing in the
governance document to read it off.

The section says two things: what the arrangement is, and where the
components are listed. It names nothing itself. The
[Licenses](https://cozystack.io/docs/v1.6/operations/configuration/licenses/)
page carries the list, and by its own framing carries upstreams only —
Cozystack-maintained components are Apache-2.0 and are not listed there
individually. Appearing on that page is therefore already the
classification, so a second list here would only drift from it.

Two components that belong on that page were missing from it despite
shipping since v1.3.4 and v1.4.3; cozystack/website#649 adds them.

Nothing moves, nothing is relicensed, no mirroring changes. This
documents the existing arrangement, which each package's `values.yaml`
already describes at its own level.

### Downstream repositories

Walked the trigger map in `docs/agents/contributing.md` against the
diff. It touches `GOVERNANCE.md` only, and no trigger concerns
governance documents — the website triggers are package additions,
platform values, variants, components, release assets,
`ApplicationDefinition` semantics, the Talos pin and developer tooling.
The website PR linked above is a companion, not a consequence of this
diff: it fixes a pre-existing gap on the Licenses page that stands on
its own.

- [x] No downstream repository is affected by this change

### Release note

```release-note
NONE
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Added governance guidance for project sub-components, including
ownership, licensing, maintenance, and repository classifications.
* Documented project infrastructure, upstream forks, private
repositories, and telemetry services.
* Clarified how third-party software is sourced, governed, mirrored, and
versioned.
* Linked to a license inventory listing third-party components and their
applicable licenses.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@kvaps

kvaps commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

@benluddy I tested your POC against the real use case, and the short answer to your question is yes — it solves it without touching Scheme.

Our apiserver registers a single Go type under about 27 kinds in one GroupVersion. On stock 0.35 both a GET and a LIST come back with the wrong kind, whichever one the scheme happens to return first. With your POC the GET is correct. Lists were still wrong, because ResourceGVK only disambiguates them when ListKind is set, and that is populated solely from the new GroupVersionListKindProvider — which nothing implements yet. I sent benluddy#5 against your branch: it derives <Kind>List when ListKind is unset, and leaves the interface as an override for anything that breaks the convention. With that, lists come back correct too.

The important part is that all of this ran on upstream apimachinery, so if your approach lands we can drop the fork we carry today and this PR is no longer needed for us. I am happy for #141344 to supersede it.

One practical note: your POC applies cleanly to the v0.35 staging module, so backporting it is realistic if anyone needs it before 1.38.

And if you are short on time, I am glad to pick the POC up and carry it through — just say the word.

Disclosure: AI-assisted (Claude) for building the test variants; I reviewed the results and the code.

kvaps added a commit to cozystack/cozystack that referenced this pull request Sep 7, 2026
…3784)

## What this PR does

Brings `GOVERNANCE.md` in line with the organisation it describes: what
the project governs, and what it ships but does not.

### Sub-projects

The `Code Repositories` list named seven repositories and was wrong in
both directions. Five of the six sub-projects whose code ships in the
distribution were missing from it — `etcd-operator`,
`keycloak-kms-proxy`, `local-ccm`, `cozystack-scheduler` and
`ingress-nginx-with-protobuf-exporter` — while `talos-bootstrap` and
`talos-meta-tool` were listed although an adopter installs neither.
Nothing stated what being on the list meant, which is why it drifted.

It is replaced by a section organised around the distinction that
decides scope: whether a repository's output reaches the artifact an
adopter installs. Packaged sub-projects get a table naming what each one
contributes, its stability, and its owners — the same handles each
repository's `CODEOWNERS` already carries. Independent tooling, project
infrastructure and upstream forks are described in a line each, as
explicitly outside that scope.

Three things an evaluator finds anyway are stated rather than left to be
discovered: that one fork (`apimachinery`) is compiled into the product,
why, and that the fix is proposed upstream as
kubernetes/kubernetes#135537; that four repositories are private, with
the reason for each, and that none of them contributes to the
distribution; and that telemetry is received by a component adopters do
not install, with a link to the opt-out.

### Third-party Dependencies

Writes down which shipped components the project does not govern.
Cozystack vendors software it does not own, which is ordinary and needs
no defence — but the boundary was nowhere stated. This is a Required
item on the Incubation checklist ("All project metadata and resources
are vendor-neutral"), and an evaluator currently has nothing in the
governance document to read it off.

The section says two things: what the arrangement is, and where the
components are listed. It names nothing itself. The
[Licenses](https://cozystack.io/docs/v1.6/operations/configuration/licenses/)
page carries the list, and by its own framing carries upstreams only —
Cozystack-maintained components are Apache-2.0 and are not listed there
individually. Appearing on that page is therefore already the
classification, so a second list here would only drift from it.

Two components that belong on that page were missing from it despite
shipping since v1.3.4 and v1.4.3; cozystack/website#649 adds them.

Nothing moves, nothing is relicensed, no mirroring changes. This
documents the existing arrangement, which each package's `values.yaml`
already describes at its own level.

### Downstream repositories

Walked the trigger map in `docs/agents/contributing.md` against the
diff. It touches `GOVERNANCE.md` only, and no trigger concerns
governance documents — the website triggers are package additions,
platform values, variants, components, release assets,
`ApplicationDefinition` semantics, the Talos pin and developer tooling.
The website PR linked above is a companion, not a consequence of this
diff: it fixes a pre-existing gap on the Licenses page that stands on
its own.

- [x] No downstream repository is affected by this change

### Release note

```release-note
NONE
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Added governance guidance for project sub-components, including
ownership, licensing, maintenance, and repository classifications.
* Documented project infrastructure, upstream forks, private
repositories, and telemetry services.
* Clarified how third-party software is sourced, governed, mirrored, and
versioned.
* Linked to a license inventory listing third-party components and their
applicable licenses.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. do-not-merge/needs-kind Indicates a PR lacks a `kind/foo` label and requires one. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. release-note Denotes a PR that will be considered when it comes time to generate release notes. sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. triage/accepted Indicates an issue or PR is ready to be actively worked on.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants