Skip to main content

ADR: Tag Passthrough from Platform Hierarchy to Cloud Resources

  • Status: Draft
  • Date: 2026-03-08
  • Decision Makers: Entigo RnD Team

Context

Organizations using Entigo Platform need to propagate custom metadata from the platform hierarchy to cloud resources. Common use cases include:

  • Cost management: cost center codes, project identifiers, budget owners
  • Compliance: data classification, regulatory tags (e.g., AWS MAP migration tags)
  • Operations: environment labels, team ownership, application identifiers

The platform hierarchy (Organization → Workspace → Zone) provides natural levels for defining this metadata. Tags defined at higher levels should inherit down to cloud resources, with lower levels able to override.

Problem Statement

The platform needs a mechanism to:

  1. Allow customers to define tags at Organization, Workspace, and Zone levels
  2. Propagate these tags to cloud resources managed within those scopes
  3. Handle character set and length differences between Kubernetes and cloud providers
  4. Provide clear filtering — not all Kubernetes labels/annotations should become cloud tags
  5. Merge tags from multiple hierarchy levels with predictable override behavior

Constraints

ConstraintKubernetes LabelsKubernetes AnnotationsAWS Tags
Value max length63 chars256 KB total256 chars
Value charset[a-zA-Z0-9._-], must start/end alphanumericAnyAny printable
SelectableYesNoVia Resource Groups Tagging API
Suitable forShort, structured valuesLong or special-character valuesBoth

Decision

1. Prefix-Based Passthrough via Labels and Annotations

Tags intended for cloud resources are defined using the prefix tags.entigo.com/ on Kubernetes labels and annotations of Organization, Workspace, and Zone objects.

The prefix serves as the filter mechanism: only labels and annotations with this prefix are propagated to cloud resources. The prefix is stripped before applying to the cloud resource.

Translation rule: tags.entigo.com/{key}: {value} → cloud tag {key}: {value}

Labels — for short, structured values

apiVersion: v1
kind: Namespace # or Zone CRD, Workspace CRD
metadata:
labels:
tags.entigo.com/cost-center: "CC-12345"
tags.entigo.com/environment: production
tags.entigo.com/team: platform-eng
tags.entigo.com/map-migrated: "MAP-d1234567890abcdef"

Results in AWS tags:

  • cost-center: CC-12345
  • environment: production
  • team: platform-eng
  • map-migrated: MAP-d1234567890abcdef

Use labels when the tag value:

  • Is 63 characters or fewer
  • Contains only [a-zA-Z0-9._-] characters
  • Benefits from Kubernetes label selectors (e.g., selecting all Zones with a given cost center)

Annotations — for long or special-character values

metadata:
annotations:
tags.entigo.com/project-description: "Project Phoenix - Q2 2026 cloud migration initiative"
tags.entigo.com/compliance-framework: "SOC2/HIPAA"

Results in AWS tags:

  • project-description: Project Phoenix - Q2 2026 cloud migration initiative
  • compliance-framework: SOC2/HIPAA

Use annotations when the tag value:

  • Exceeds 63 characters
  • Contains characters not allowed in Kubernetes label values (spaces, /, :, etc.)
  • Does not need to be selectable via label selectors

Conflict Resolution

If the same key exists in both labels and annotations (e.g., both labels: tags.entigo.com/team and annotations: tags.entigo.com/team), the annotation value wins. This is because annotations are the more permissive format — if a user placed the same key in both locations, the annotation likely contains the more accurate value.

2. Tag Inheritance Hierarchy

Tags propagate down the platform hierarchy with child overriding parent on conflict:

Organization tags
↓ inherited by all Workspaces
Workspace tags
↓ inherited by all Zones
Zone tags
↓ applied to all cloud resources in the Zone

Merge precedence (highest priority first):

  1. Zone-level tags.entigo.com/* — most specific, wins on conflict
  2. Workspace-level tags.entigo.com/*
  3. Organization-level tags.entigo.com/*

Example:

# Organization
labels:
tags.entigo.com/company: acme-corp
tags.entigo.com/cost-center: CC-DEFAULT

# Workspace
labels:
tags.entigo.com/environment: production
tags.entigo.com/cost-center: CC-PROD # overrides Organization

# Zone
labels:
tags.entigo.com/cost-center: CC-TEAM-A # overrides Workspace and Organization
tags.entigo.com/team: analytics

Effective tags on cloud resources in this Zone:

  • company: acme-corp (from Organization)
  • environment: production (from Workspace)
  • cost-center: CC-TEAM-A (from Zone, overrides)
  • team: analytics (from Zone)

3. Validation

The platform validates passthrough tags at admission time:

  1. Tag key format: after prefix stripping, the key must be valid for the target cloud provider(s) associated with the workspace
  2. Tag value format: the value must be valid for the target cloud provider (e.g., AWS allows 256 chars; GCP labels allow only 63 chars lowercase)
  3. Tag budget: total tags (platform + Crossplane + passthrough) must not exceed the provider limit (50 for AWS/Azure)
  4. Reserved keys: passthrough tag keys must not collide with platform tag keys. For example, tags.entigo.com/workspace is rejected because workspace is a platform-reserved key (entigo:workspace on AWS)
  5. Provider-specific characters: the platform warns or rejects tag keys/values that contain characters invalid for the workspace's cloud provider

4. Reserved Platform Tags

The platform manages a set of internal tags on every cloud resource. These tags are critical for ABAC-based permission boundaries, resource isolation, and Crossplane reconciliation. Users must not be able to override them through the passthrough mechanism.

Reserved tag prefixes and keys:

Tag KeyPurposeExample Value
entigo:workspaceWorkspace isolation and ABACws-prod-001
entigo:zoneZone-level resource scopingzone-1
Any entigo:* keyPlatform-internal metadata
crossplane-kindCrossplane system tag (immutable) — links AWS resource to Kubernetes kindbucket.s3.aws.upbound.io
crossplane-nameCrossplane system tag (immutable) — links AWS resource to Kubernetes managed resource namemy-bucket-abcde
crossplane-providerconfigCrossplane system tag (immutable) — identifies the provider configurationdefault

Behavior when a user sets a reserved tag:

  1. Admission rejection (preferred): If a user adds tags.entigo.com/workspace (which would resolve to a cloud tag workspace colliding with entigo:workspace), the admission webhook rejects the request with a clear error message.
  2. Silent discard (fallback): If a reserved tag somehow reaches the tag-merge controller (e.g., via direct API access bypassing admission), the controller ignores the user-supplied value and applies the platform-computed value instead. The user-supplied value is never propagated to cloud resources.

This applies regardless of hierarchy level — even Organization-level passthrough tags cannot override platform-internal tags.

Rationale: Platform tags like entigo:workspace underpin IAM permission boundaries. Allowing users to override them would break workspace isolation and could grant cross-workspace access to resources.

5. Implementation in Crossplane Compositions

Passthrough tags are merged into spec.forProvider.tags on Crossplane managed resources through Composition patches:

patches:
# Merge Zone-level passthrough tags (highest priority)
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.passthroughTags
toFieldPath: spec.forProvider.tags
policy:
mergeOptions:
keepMapValues: true # preserves platform tags already set

The platform controller resolves the inheritance hierarchy and computes the effective passthrough tags before they reach the Composition. The Composition receives a pre-merged passthroughTags map.

6. Cost Allocation Tag Activation

AWS requires cost allocation tags to be explicitly activated in the Billing console before they appear in Cost & Usage Reports. This is a manual step with a 24-hour activation delay.

The platform should:

  • Document this requirement clearly in user guides
  • Provide a list of active passthrough tag keys that customers should activate
  • Consider automating activation via the AWS Cost Allocation Tags API in future iterations

Alternatives Considered

Alternative 1: Dedicated spec.tags Field on Zone CRD

Approach: Add a structured spec.tags field to the Zone (and Workspace/Organization) CRD, with explicit schema for passthrough tags.

spec:
tags:
cost-center: "CC-12345"
environment: production

Pros:

  • Full control over validation schema
  • No character set limitations (CRD field, not a label)
  • Explicit — clearly separate from Kubernetes metadata

Cons:

  • Requires CRD schema changes for every tag-related feature
  • Cannot use Kubernetes label selectors on tag values
  • Less familiar UX — users must edit spec instead of adding labels
  • Not inheritable via standard Kubernetes mechanisms; requires custom controller logic regardless

Rejected: The UX disadvantage outweighs the character set benefit. Labels and annotations are the standard Kubernetes interface for metadata, and the prefix convention provides clear filtering.

Alternative 2: Propagate All Labels to Cloud Resources

Approach: Forward every Kubernetes label on the Zone object to cloud resources, without prefix filtering.

Pros:

  • Simplest implementation — no prefix logic needed
  • Users don't need to learn a prefix convention

Cons:

  • Kubernetes system labels (app.kubernetes.io/managed-by, etc.) would leak to cloud resources
  • No way to have Kubernetes-only labels that don't propagate
  • Would quickly consume the 50-tag budget with irrelevant labels
  • Cloud provider character restrictions would cause failures for valid Kubernetes labels

Rejected: Uncontrolled propagation creates tag pollution and budget exhaustion. Explicit filtering is essential.

Alternative 3: Separate Whitelist Configuration

Approach: Define a separate ConfigMap or CRD that lists which tag keys are allowed to propagate.

apiVersion: config.entigo.com/v1alpha1
kind: TagPassthroughPolicy
spec:
allowedKeys:
- cost-center
- environment
- team

Pros:

  • Central control over which tags propagate
  • Can be managed by platform administrators independently of Zone authors

Cons:

  • Additional configuration object to manage
  • Every new tag key requires updating the whitelist — friction for self-service teams
  • Splits tag definition (on Zone) from tag authorization (on policy) — harder to reason about

Rejected: The prefix convention (tags.entigo.com/*) achieves the same filtering without a separate configuration object. Teams can self-service by adding labels without waiting for whitelist updates.

Alternative 4: Labels Only (No Annotations)

Approach: Use only Kubernetes labels with the tags.entigo.com/ prefix for passthrough.

Pros:

  • Single mechanism — simpler mental model
  • Label selectors work on all passthrough tags

Cons:

  • Value length limited to 63 characters — too short for some legitimate tag values (project descriptions, compliance identifiers)
  • Character set restricted — cannot contain spaces, /, :, or other characters common in AWS tag values
  • Would force users to truncate or encode values, creating translation burden

Rejected: The 63-character and character-set limitations make labels insufficient as the sole mechanism. Annotations provide the escape hatch for values that don't fit label constraints.

Consequences

Positive

  1. Self-service: teams can add passthrough tags by simply adding labels/annotations with the prefix — no configuration changes or admin approval needed
  2. Familiar UX: uses standard Kubernetes labels and annotations, not custom CRD fields
  3. Clear filtering: the prefix convention makes it obvious which metadata propagates to cloud resources
  4. Flexible values: labels handle short values; annotations handle long or special-character values
  5. Predictable inheritance: child overrides parent is intuitive and well-understood
  6. Label selectors: teams can query Zones by passthrough tag values (e.g., find all Zones with a given cost center)

Negative

  1. Dual mechanism: users need to understand when to use labels vs. annotations — potential source of confusion
  2. Prefix verbosity: tags.entigo.com/cost-center is longer than just cost-center — adds visual noise
  3. Validation complexity: the platform must validate tag values against the target cloud provider's constraints, which differ per provider
  4. Annotation-wins rule: the conflict resolution between labels and annotations may surprise users who set both

Neutral

  1. Cost allocation activation remains a manual AWS step — the platform can document and eventually automate this
  2. Tag inheritance resolution is performed by the platform controller, adding minimal computational overhead
  3. Crossplane reconciliation ensures passthrough tags on cloud resources stay in sync with the Kubernetes source of truth

References