labels: a caller's requirement is a conjunction, as the egress floor is - #539
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
Code Review
This pull request changes the matching logic for required labels (such as X-Sam-Required-Labels and SDK requiredLabels) from a disjunction (any-of) to a conjunction (all-of), aligning it with the operator's egress floor matching rule. The separate LabelFloorCheck, LabelsSatisfyFloor, and LabelsContradictFloor functions are consolidated into unified LabelCheck, LabelsSatisfy, and LabelsContradict functions in the api package. Documentation, command-line interfaces, Go/JS/Python SDKs, and their respective unit/integration tests have been updated to reflect and verify this conjunction behavior. I have no feedback to provide as there are no review comments to assess.
`X-Sam-Required-Labels: k=v,k2=v2` and the SDKs' `requiredLabels` /
`required_labels` now refuse a provider unless its credential attests
every listed pair. Until now they accepted any one pair, while
`egress.require_labels` required all of them: one syntax, two meanings.
The disjunction dates from `X-Sam-Required-Region`, which took a list of
regions (`region("EU") or region("EU-DE")`). Generalised to a map with one
value per key it can no longer spell the alternative it was made for
(`region=eu-west-1` or `region=eu-central-1` is a `400 duplicate label
key`), and the intent people do write with two pairs, `region=eu,
compliance=gdpr`, was silently widened to "either": a provider attesting
only `region=eu` passed, with no error. Every label selector a reader is
likely to know (Kubernetes, Prometheus, Istio, SPIRE, Docker, Nomad, AWS
IAM) reads a map of pairs as a conjunction and spells alternatives for one
key explicitly.
`api.LabelCheck` compiles `check if label(k1, v1), label(k2, v2)`;
`LabelFloorCheck` is gone, the floor uses the same function.
`LabelsSatisfyFloor` / `LabelsContradictFloor` become `LabelsSatisfy` /
`LabelsContradict` and serve the requirement in `rankProviders` the way
they served the floor: a local must declare every pair, a remote is only
dropped on a conflicting claim and the gate decides on attested facts.
`checkPeerLabels` keeps two checks so no caller input reaches the floor.
A repeated key (`region=a,region=b`) stays rejected and is reserved for
per-key alternatives later; adding it then breaks no client.
aojea
force-pushed
the
required-labels-conjunction
branch
from
September 28, 2026 17:25
5a21468 to
6443c66
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
X-Sam-Required-Labels: k=v,k2=v2and the SDKs'requiredLabels/required_labelsnow refuse a provider unless its credential attests every listed pair. Until now they accepted any one pair, whileegress.require_labelsrequired all of them: one syntax, two meanings. This settles the caller's requirement on the same rule as the floor before the release freezes it.Where the disjunction came from
bda7e72 introduced
X-Sam-Required-Regionwith a list of regions:check if region("EU") or region("EU-DE"). "Any of these regions" is the natural reading of a list of values for one key. The header was then generalised to labels, a map with one value per key (parseRequiredLabelsrejects a duplicate key), and theorcame along. With a map, the disjunction can only express cross-key alternatives ("in eu, or on team platform"), and neither of the two things people write two pairs for is expressible:region=euregion=eu,compliance=gdprmeaning bothregion=eupasses, silentlygpu=true,family=llamaregion=eu-west-1orregion=eu-central-1400 duplicate label keyteam=platformorregion=euPrior art
Every label selector a reader is likely to know reads a map or comma-list of pairs as a conjunction and spells alternatives for one key explicitly:
matchLabels,-l a=x,b=ykey in (a, b){a="x",b="y"}=~"a|b"DestinationRulesubsetlabels, Envoy subset metadata--constraint, NomadconstraintConditionForAnyValueon list valuesNetworkPolicyfromentry, OR across entries (a list)No system was found where a map of pairs means "any of them". SAM's own
served_byalready follows the convention: it is arepeated stringlist, documented as any-of, and it can holdsite=euandsite=us. Provider-sideattenuation.checksare ANDed. The caller's map was the one outlier.Why the conjunction
403on the first request. Under AND, adding a pair only narrows; under OR, adding a pair only widens. A fail-closed gate should be monotone in the safe direction.node-api.md. They collapse to one sentence.What changes
api.LabelCheckcompilescheck if label(k1, v1), label(k2, v2).LabelFloorCheckis gone; the floor uses the same function.LabelsSatisfyFloor/LabelsContradictFloorbecomeLabelsSatisfy/LabelsContradict.checkPeerLabelskeeps two checks so no caller input reaches the floor; both are conjunctions and the authorizer ANDs them.rankProviderstreats the requirement as it treated the floor: a local must declare every pair; a remote is dropped early only on a claim that conflicts, and the gate decides on attested facts.requireLabels/require_labels(from sdk: an egress floor, stated at join and held on every call #538:requireEgressLabels/require_egress_labels) share one predicate, every pair must be attested; they differ only in the message a refusal carries ("does not attest every required label" / "does not attest the egress floor").LabelsNotSatisfiedErrortakes that message as a required argument.authorization.md,boundaries.md,networking.md,exposing-services.md,native-sdks.md,node-api.md,node-config.md,sdk/README.md, both skills.api,internal/node, both SDKs and theTestNativeSDKsMeshmatrix now expect a refusal, with new cases for every pair attested and for one pair of two missing.rankProvidersgets a two-pair case (remote silent on a pair survives to the gate; a conflicting one and a local short of a pair do not). The e2e bats tests use single pairs and are unaffected.Reserved for later
A repeated key,
region=eu-west-1,region=eu-central-1, stays a400and is reserved for per-key alternatives: AND across keys, OR within a key, as Kubernetesinand Prometheus=~. Adding it later breaks no client, and RFC 9110 folding of repeated header lines produces exactly this form. The SDK shape would beRecord<string, string | string[]>.|is not an option: it is a legal value character inapi/labels.go.Verified
Rebased on #538.
go build ./...,go vet,go test ./api/ ./internal/node/, JStypecheck+mcp.test+session.test, Pythontest_mcp.py+test_session.py+test_libp2p_http.py, and the wholego test ./tests/integration -run 'TestNativeSDKsMesh$'(the label matrix in both directions and #538'segress-floorsubtest) pass locally.