Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions agents/skills/sam-a2a-bridge/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ input schema) and `file_path` attachments appropriately.
under the download directory.
- **Data residency**: when the task involves data that must stay in a region or
jurisdiction, set `required_labels` (comma-separated `key=value`, e.g.
`region=eu-west-1`). The local node then refuses fail-closed before any data
leaves it unless the peer's control-plane-attested labels match. Never drop
`region=eu-west-1`; several pairs must all be attested). The local node then
refuses fail-closed before any data leaves it unless the peer's
control-plane-attested labels match. Never drop
or weaken `required_labels` to make a refused call succeed without the
user's explicit approval — the refusal is the feature.

Expand Down
8 changes: 4 additions & 4 deletions agents/skills/sam-mesh/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,10 @@ The node exposes them through an OpenAI-compatible facade on its own address:
- `GET /v1/models` lists the models reachable across the mesh.
- `POST /v1/chat/completions` routes to a provider of the requested `model`,
preferring a local one, and fails over between providers.
- Add `X-Sam-Required-Labels: key=value` (comma-separated, any-of) to accept
only providers whose labels the control plane attested, for example
`region=eu`. Enforcement is fail-closed: unattested providers are rejected
before any request data leaves the node.
- Add `X-Sam-Required-Labels: key=value` (comma-separated; every pair must
be attested) to accept only providers whose labels the control plane
attested, for example `region=eu`. Enforcement is fail-closed: unattested
providers are rejected before any request data leaves the node.

To pin one specific provider instead of letting the facade choose, call
`discover_remote_services` with `{"type":"inference"}` and send the request to
Expand Down
85 changes: 30 additions & 55 deletions api/datalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -737,47 +737,22 @@ func LabelFacts(labels map[string]string) []biscuit.Fact {
}

// LabelCheck compiles a required label set (canonical, pre-validated with
// ValidateLabels) into a single fail-closed check satisfied when the token
// carries any of them: `check if label("region", "us-east-1") or
// label("team", "platform")`.
func LabelCheck(required map[string]string) (biscuit.Check, error) {
if len(required) == 0 {
return biscuit.Check{}, fmt.Errorf("no required labels")
}
keys := make([]string, 0, len(required))
for k := range required {
keys = append(keys, k)
}
sort.Strings(keys)
clauses := make([]string, 0, len(required))
for _, k := range keys {
if err := ValidateLabelKey(k); err != nil {
return biscuit.Check{}, err
}
v := required[k]
if err := ValidateLabelValue(v); err != nil {
return biscuit.Check{}, err
}
clauses = append(clauses, fmt.Sprintf("%s(%q, %q)", FactLabel, k, v))
}
return parser.FromStringCheck("check if " + strings.Join(clauses, " or "))
}

// LabelFloorCheck compiles an operator's egress floor (see Egress.RequireLabels)
// into a single fail-closed check satisfied only when the token carries *every*
// pair: `check if label("jurisdiction", "eu"), label("compliance", "gdpr")`.
// ValidateLabels) into a single fail-closed check satisfied only when the
// token carries *every* pair: `check if label("jurisdiction", "eu"),
// label("compliance", "gdpr")`.
//
// The conjunction is the whole difference from LabelCheck, which is a
// disjunction because a caller naming several labels means "any of these will
// do". A floor cannot mean that: a peer attesting only the most permissive of
// several alternatives would satisfy the floor while sitting outside the
// boundary the operator drew. So a floor takes a map — one value per key, no
// way to spell an alternative — and requires all of it.
// The same rule serves a caller's requirement (X-Sam-Required-Labels, an
// SDK's required labels) and an operator's egress floor (Egress.RequireLabels).
// A map gives one value per key, so neither can spell an alternative, and
// listing several pairs narrows the set of acceptable peers, as it does in
// every label selector a reader is likely to know. A disjunction would let
// a peer attesting the most permissive pair stand in for the rest, and a
// caller who read the list as a conjunction would send data to a peer that
// lacks a property they required, with no error to tell them.
//
// Both are ordinary Biscuit checks, so a caller's requirement and a floor are
// combined by adding each to the authorizer and letting it AND them; neither
// needs to know about the other.
func LabelFloorCheck(required map[string]string) (biscuit.Check, error) {
// A requirement and a floor are combined by adding one check each to the
// authorizer, which ANDs them; neither needs to know about the other.
func LabelCheck(required map[string]string) (biscuit.Check, error) {
if len(required) == 0 {
return biscuit.Check{}, fmt.Errorf("no required labels")
}
Expand All @@ -800,31 +775,31 @@ func LabelFloorCheck(required map[string]string) (biscuit.Check, error) {
return parser.FromStringCheck("check if " + strings.Join(clauses, ", "))
}

// LabelsSatisfyFloor reports whether claimed satisfies every pair of the floor.
// It is the non-attested counterpart of LabelFloorCheck, for the one provider
// class that has no Biscuit to check: a service local to this node, whose
// labels are its own configuration and so are complete. An empty floor is
// satisfied by anything, so callers may pass one unconditionally.
func LabelsSatisfyFloor(floor, claimed map[string]string) bool {
for k, v := range floor {
// LabelsSatisfy reports whether claimed carries every pair of required. It is
// the non-attested counterpart of LabelCheck, for the one provider class that
// has no Biscuit to check: a service local to this node, whose labels are its
// own configuration and so are complete. An empty requirement is satisfied by
// anything, so callers may pass one unconditionally.
func LabelsSatisfy(required, claimed map[string]string) bool {
for k, v := range required {
if claimed[k] != v {
return false
}
}
return true
}

// LabelsContradictFloor reports whether claimed states a *different* value for
// some key the floor requires.
// LabelsContradict reports whether claimed states a *different* value for
// some key required names.
//
// Absence is not contradiction, which is the whole distinction from
// LabelsSatisfyFloor. Gossiped claims are a discovery hint and may carry only
// part of what a peer attests, so a peer silent on one pair of the floor may
// still satisfy all of it in its Biscuit. Only a conflicting value is grounds
// to skip such a peer before the gate has seen its attested facts; treating
// silence as failure would drop providers that are inside the boundary.
func LabelsContradictFloor(floor, claimed map[string]string) bool {
for k, v := range floor {
// LabelsSatisfy. Gossiped claims are a discovery hint and may carry only part
// of what a peer attests, so a peer silent on one pair may still satisfy all
// of them in its Biscuit. Only a conflicting value is grounds to skip such a
// peer before the gate has seen its attested facts; treating silence as
// failure would drop providers that are inside the boundary.
func LabelsContradict(required, claimed map[string]string) bool {
for k, v := range required {
if got, stated := claimed[k]; stated && got != v {
return true
}
Expand Down
91 changes: 38 additions & 53 deletions api/datalog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,12 +573,6 @@ func TestLabelFactsAndCheck(t *testing.T) {
if facts := LabelFacts(nil); facts != nil {
t.Errorf("LabelFacts(nil) = %v, want nil", facts)
}
if _, err := LabelCheck(nil); err == nil {
t.Error("LabelCheck(nil): expected error, got nil")
}
if _, err := LabelCheck(map[string]string{"region": "bad,value"}); err == nil {
t.Error("LabelCheck(invalid value): expected error, got nil")
}

tests := []struct {
name string
Expand All @@ -587,8 +581,9 @@ func TestLabelFactsAndCheck(t *testing.T) {
expectAllow bool
}{
{"exact match", map[string]string{"region": "us-east-1"}, map[string]string{"region": "us-east-1"}, true},
{"any-of requirement", map[string]string{"region": "us-east-1"}, map[string]string{"region": "eu", "team": "us-east-1"}, false},
{"any-of requirement matches one key", map[string]string{"region": "us-east-1", "team": "platform"}, map[string]string{"region": "eu", "team": "platform"}, true},
{"every pair present", map[string]string{"region": "us-east-1", "team": "platform", "tier": "gold"}, map[string]string{"region": "us-east-1", "team": "platform"}, true},
{"one pair of two missing", map[string]string{"region": "us-east-1"}, map[string]string{"region": "us-east-1", "team": "platform"}, false},
{"one pair of two wrong", map[string]string{"region": "us-east-1", "team": "platform"}, map[string]string{"region": "eu", "team": "platform"}, false},
{"case-sensitive value mismatch", map[string]string{"region": "us-east-1"}, map[string]string{"region": "US-EAST-1"}, false},
{"no built-in hierarchy: coarser requirement does not match a finer claim", map[string]string{"region": "us-east-1"}, map[string]string{"region": "us"}, false},
{"disjoint labels", map[string]string{"region": "us-east-1"}, map[string]string{"region": "eu-west-1"}, false},
Expand Down Expand Up @@ -629,48 +624,38 @@ func TestLabelFactsAndCheck(t *testing.T) {
}
}

// A caller's requirement is a disjunction ("any of these will do") and an
// operator's egress floor is a conjunction ("all of these"). The difference is
// the reason both exist, so it is pinned on the compiled shape rather than on
// the rendered string: a disjunction becomes one query body per alternative, a
// conjunction one body carrying every predicate.
func TestLabelFloorCheckIsConjunctionUnlikeLabelCheck(t *testing.T) {
// A requirement of several pairs is a conjunction, pinned on the compiled
// shape rather than on the rendered string: one query body carrying every
// predicate, not one body per pair.
func TestLabelCheckIsConjunction(t *testing.T) {
both := map[string]string{"jurisdiction": "eu", "compliance": "gdpr"}

floor, err := LabelFloorCheck(both)
if err != nil {
t.Fatalf("LabelFloorCheck: %v", err)
}
if len(floor.Queries) != 1 {
t.Fatalf("a floor must compile to a single conjunctive body, got %d alternatives", len(floor.Queries))
}
if got := len(floor.Queries[0].Body); got != len(both) {
t.Errorf("the floor's body carries %d predicates, want all %d", got, len(both))
}

caller, err := LabelCheck(both)
check, err := LabelCheck(both)
if err != nil {
t.Fatalf("LabelCheck: %v", err)
}
if len(caller.Queries) != len(both) {
t.Errorf("a caller requirement must compile to one body per alternative, got %d", len(caller.Queries))
if len(check.Queries) != 1 {
t.Fatalf("a requirement must compile to a single conjunctive body, got %d alternatives", len(check.Queries))
}
if got := len(check.Queries[0].Body); got != len(both) {
t.Errorf("the body carries %d predicates, want all %d", got, len(both))
}
}

func TestLabelFloorCheckRejectsBadInput(t *testing.T) {
if _, err := LabelFloorCheck(nil); err == nil {
t.Error("LabelFloorCheck(nil): expected error, got nil")
func TestLabelCheckRejectsBadInput(t *testing.T) {
if _, err := LabelCheck(nil); err == nil {
t.Error("LabelCheck(nil): expected error, got nil")
}
if _, err := LabelFloorCheck(map[string]string{"region": "bad,value"}); err == nil {
t.Error("LabelFloorCheck(invalid value): expected error, got nil")
if _, err := LabelCheck(map[string]string{"region": "bad,value"}); err == nil {
t.Error("LabelCheck(invalid value): expected error, got nil")
}
if _, err := LabelFloorCheck(map[string]string{"bad key!": "v"}); err == nil {
t.Error("LabelFloorCheck(invalid key): expected error, got nil")
if _, err := LabelCheck(map[string]string{"bad key!": "v"}); err == nil {
t.Error("LabelCheck(invalid key): expected error, got nil")
}
}

func TestLabelsSatisfyFloor(t *testing.T) {
floor := map[string]string{"jurisdiction": "eu", "compliance": "gdpr"}
func TestLabelsSatisfy(t *testing.T) {
required := map[string]string{"jurisdiction": "eu", "compliance": "gdpr"}
tests := []struct {
name string
claimed map[string]string
Expand All @@ -682,39 +667,39 @@ func TestLabelsSatisfyFloor(t *testing.T) {
{"nothing claimed", nil, false},
}
for _, tt := range tests {
if got := LabelsSatisfyFloor(floor, tt.claimed); got != tt.want {
t.Errorf("%s: LabelsSatisfyFloor = %v, want %v", tt.name, got, tt.want)
if got := LabelsSatisfy(required, tt.claimed); got != tt.want {
t.Errorf("%s: LabelsSatisfy = %v, want %v", tt.name, got, tt.want)
}
}
// An empty floor constrains nothing, so callers can pass it unconditionally.
if !LabelsSatisfyFloor(nil, nil) {
t.Error("an empty floor must be satisfied by anything")
// An empty requirement constrains nothing, so callers can pass it unconditionally.
if !LabelsSatisfy(nil, nil) {
t.Error("an empty requirement must be satisfied by anything")
}
}

// The two floor predicates differ on one case, and it is the case that
// matters: a peer silent on a pair the floor requires. Gossip is partial, so
// silence must not read as failure before the gate has seen attested facts.
func TestLabelsContradictFloorTreatsSilenceAsUnknown(t *testing.T) {
floor := map[string]string{"jurisdiction": "eu", "compliance": "gdpr"}
// The two predicates differ on one case, and it is the case that matters: a
// peer silent on a pair the requirement names. Gossip is partial, so silence
// must not read as failure before the gate has seen attested facts.
func TestLabelsContradictTreatsSilenceAsUnknown(t *testing.T) {
required := map[string]string{"jurisdiction": "eu", "compliance": "gdpr"}

partial := map[string]string{"jurisdiction": "eu"}
if LabelsContradictFloor(floor, partial) {
t.Error("a claim silent on one pair does not contradict the floor")
if LabelsContradict(required, partial) {
t.Error("a claim silent on one pair does not contradict the requirement")
}
if LabelsSatisfyFloor(floor, partial) {
if LabelsSatisfy(required, partial) {
t.Error("but it does not satisfy it either")
}

conflicting := map[string]string{"jurisdiction": "us"}
if !LabelsContradictFloor(floor, conflicting) {
if !LabelsContradict(required, conflicting) {
t.Error("a different value for a required key is a contradiction")
}

if LabelsContradictFloor(floor, nil) {
if LabelsContradict(required, nil) {
t.Error("no claims at all cannot contradict anything")
}
if LabelsContradictFloor(floor, map[string]string{"jurisdiction": "eu", "compliance": "gdpr"}) {
if LabelsContradict(required, map[string]string{"jurisdiction": "eu", "compliance": "gdpr"}) {
t.Error("a fully satisfying claim must not read as a contradiction")
}
}
4 changes: 2 additions & 2 deletions api/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,8 @@ const (
HeaderSamNoTrailingSlash = "X-Sam-No-Trailing-Slash"

// HeaderSamRequiredLabels constrains an inference request on the sidecar's
// OpenAI-compatible endpoints (/v1/*) to providers attested with any of a
// comma-separated list of "key=value" label requirements (see
// OpenAI-compatible endpoints (/v1/*) to providers attested with every
// pair of a comma-separated list of "key=value" label requirements (see
// api/labels.go and LabelCheck); invalid entries are rejected with HTTP
// 400. It can only narrow what mesh policy allows, never widen it. Absent
// means any provider permitted by policy.
Expand Down
9 changes: 4 additions & 5 deletions api/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,10 @@ type Egress struct {
// whatever the caller supplied, and a caller that supplies nothing is
// unconstrained.
//
// Every pair must hold (AND), unlike a caller's requirement, where any one
// pair is enough (see LabelCheck vs LabelFloorCheck). A map gives one value
// per key, so a floor cannot express alternatives — that is the point: a
// floor with alternatives would let the weakest of them stand in for the
// rest.
// Every pair must hold, as for a caller's requirement (see LabelCheck). A
// map gives one value per key, so a floor cannot express alternatives —
// that is the point: a floor with alternatives would let the weakest of
// them stand in for the rest.
//
// A floor naming a label no peer attests reaches nothing, which is a
// usable egress kill switch.
Expand Down
2 changes: 1 addition & 1 deletion cmd/sam-a2a-bridge/a2a.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ type sendAgentTaskParams struct {
Data map[string]any `json:"data,omitempty" jsonschema:"Structured JSON payload sent to the agent as an A2A DataPart"`
FilePath string `json:"file_path,omitempty" jsonschema:"Local file to attach; sent to the agent as bytes, max 5 MB"`
FileName string `json:"file_name,omitempty" jsonschema:"Name shown to the agent for the attached file (default: the file's base name)"`
RequiredLabels string `json:"required_labels,omitempty" jsonschema:"Comma-separated key=value labels the provider must have attested (e.g. region=eu-west-1); the local node refuses fail-closed before any data leaves it"`
RequiredLabels string `json:"required_labels,omitempty" jsonschema:"Comma-separated key=value labels the provider must all have attested (e.g. region=eu-west-1,compliance=gdpr); the local node refuses fail-closed before any data leaves it"`
ContextID string `json:"context_id,omitempty" jsonschema:"Continue an existing conversation context"`
TaskID string `json:"task_id,omitempty" jsonschema:"Reply into an existing task, e.g. one in state input-required"`
}
Expand Down
2 changes: 1 addition & 1 deletion internal/node/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func CompleteNodeConfig(config api.NodeConfig) (*NodeConfigComplete, error) {
if err := api.ValidateLabels(config.Egress.RequireLabels); err != nil {
return nil, fmt.Errorf("invalid egress.require_labels: %w", err)
}
if _, err := api.LabelFloorCheck(config.Egress.RequireLabels); err != nil {
if _, err := api.LabelCheck(config.Egress.RequireLabels); err != nil {
return nil, fmt.Errorf("invalid egress.require_labels: %w", err)
}
complete.EgressRequireLabels = config.Egress.RequireLabels
Expand Down
17 changes: 8 additions & 9 deletions internal/node/labels_gate.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ func (n *SamNode) egressFloor() map[string]string {
}

// VerifyPeerLabels ensures the peer holds control-plane-attested labels
// satisfying the caller's requirement (any one pair) and the operator's egress
// floor (every pair). Positive verdicts are cached.
// satisfying every pair of the caller's requirement and every pair of the
// operator's egress floor. Positive verdicts are cached.
//
// The gate runs whenever either exists. A caller that requires nothing is
// still held to the floor — the whole point of a floor is that the party being
Expand Down Expand Up @@ -198,10 +198,9 @@ func (n *SamNode) checkPeerLabels(providerBiscuit []byte, peerID peer.ID, requir
if err != nil {
return fmt.Errorf("provider %s authorizer instantiation failed: %w", peerID, err)
}
// Two checks, not one merged set. The authorizer ANDs them, so the peer
// has to satisfy both independently; merging them into a single
// disjunction would let the caller's pairs stand in for the floor's and
// widen exactly what the floor exists to bound.
// Two checks, not one merged set: the floor is the operator's and no
// caller input reaches it. Both are conjunctions, and the authorizer ANDs
// them, so the peer has to attest every pair of each.
if len(required) > 0 {
check, err := api.LabelCheck(required)
if err != nil {
Expand All @@ -210,7 +209,7 @@ func (n *SamNode) checkPeerLabels(providerBiscuit []byte, peerID peer.ID, requir
authorizer.AddCheck(check)
}
if len(floor) > 0 {
check, err := api.LabelFloorCheck(floor)
check, err := api.LabelCheck(floor)
if err != nil {
return err
}
Expand All @@ -219,9 +218,9 @@ func (n *SamNode) checkPeerLabels(providerBiscuit []byte, peerID peer.ID, requir
authorizer.AddPolicy(api.AllowIfTruePolicy)
if err := authorizer.Authorize(); err != nil {
if len(floor) > 0 {
return fmt.Errorf("provider %s does not attest the caller requirement %v and the egress floor %v: %w", peerID, required, floor, err)
return fmt.Errorf("provider %s does not attest every label of the caller requirement %v and the egress floor %v: %w", peerID, required, floor, err)
}
return fmt.Errorf("provider %s has no attested label matching %v: %w", peerID, required, err)
return fmt.Errorf("provider %s does not attest every label of %v: %w", peerID, required, err)
}
return nil
}
Expand Down
Loading
Loading