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:
- Allow customers to define tags at Organization, Workspace, and Zone levels
- Propagate these tags to cloud resources managed within those scopes
- Handle character set and length differences between Kubernetes and cloud providers
- Provide clear filtering — not all Kubernetes labels/annotations should become cloud tags
- Merge tags from multiple hierarchy levels with predictable override behavior
Constraints
| Constraint | Kubernetes Labels | Kubernetes Annotations | AWS Tags |
|---|---|---|---|
| Value max length | 63 chars | 256 KB total | 256 chars |
| Value charset | [a-zA-Z0-9._-], must start/end alphanumeric | Any | Any printable |
| Selectable | Yes | No | Via Resource Groups Tagging API |
| Suitable for | Short, structured values | Long or special-character values | Both |
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-12345environment: productionteam: platform-engmap-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 initiativecompliance-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):
- Zone-level
tags.entigo.com/*— most specific, wins on conflict - Workspace-level
tags.entigo.com/* - 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:
- Tag key format: after prefix stripping, the key must be valid for the target cloud provider(s) associated with the workspace
- 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)
- Tag budget: total tags (platform + Crossplane + passthrough) must not exceed the provider limit (50 for AWS/Azure)
- Reserved keys: passthrough tag keys must not collide with platform tag keys. For example,
tags.entigo.com/workspaceis rejected becauseworkspaceis a platform-reserved key (entigo:workspaceon AWS) - 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 Key | Purpose | Example Value |
|---|---|---|
entigo:workspace | Workspace isolation and ABAC | ws-prod-001 |
entigo:zone | Zone-level resource scoping | zone-1 |
Any entigo:* key | Platform-internal metadata | — |
crossplane-kind | Crossplane system tag (immutable) — links AWS resource to Kubernetes kind | bucket.s3.aws.upbound.io |
crossplane-name | Crossplane system tag (immutable) — links AWS resource to Kubernetes managed resource name | my-bucket-abcde |
crossplane-providerconfig | Crossplane system tag (immutable) — identifies the provider configuration | default |
Behavior when a user sets a reserved tag:
- Admission rejection (preferred): If a user adds
tags.entigo.com/workspace(which would resolve to a cloud tagworkspacecolliding withentigo:workspace), the admission webhook rejects the request with a clear error message. - 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
specinstead 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
- Self-service: teams can add passthrough tags by simply adding labels/annotations with the prefix — no configuration changes or admin approval needed
- Familiar UX: uses standard Kubernetes labels and annotations, not custom CRD fields
- Clear filtering: the prefix convention makes it obvious which metadata propagates to cloud resources
- Flexible values: labels handle short values; annotations handle long or special-character values
- Predictable inheritance: child overrides parent is intuitive and well-understood
- Label selectors: teams can query Zones by passthrough tag values (e.g., find all Zones with a given cost center)
Negative
- Dual mechanism: users need to understand when to use labels vs. annotations — potential source of confusion
- Prefix verbosity:
tags.entigo.com/cost-centeris longer than justcost-center— adds visual noise - Validation complexity: the platform must validate tag values against the target cloud provider's constraints, which differ per provider
- Annotation-wins rule: the conflict resolution between labels and annotations may surprise users who set both
Neutral
- Cost allocation activation remains a manual AWS step — the platform can document and eventually automate this
- Tag inheritance resolution is performed by the platform controller, adding minimal computational overhead
- Crossplane reconciliation ensures passthrough tags on cloud resources stay in sync with the Kubernetes source of truth