Decisions

ADR-0006: AWS Resource Tagging Taxonomy

Decision to establish a mandatory three-tier tagging taxonomy for all AWS resources to enable cost allocation and operational traceability.

ApprovedNo MCP
FieldValue
StatusApproved
Date2026-03-12
AuthorsEngineering
Replaces

Context

Ontopix operates with a single AWS account hosting workloads across multiple products, client engagements, and internal initiatives. Without a shared tagging standard, AWS costs are undifferentiated: there is no reliable way to attribute spend to a product, a client, or a team; no way to reconcile infrastructure costs against client invoices; and no way to trace a running resource back to the code that created it.

This ADR establishes a mandatory tagging taxonomy that addresses two complementary concerns:

  • Cost allocation — attributing AWS spend to the correct product, billing mode, client, and internal cost center.
  • Operational traceability — identifying who owns a resource and what code manages it.

The taxonomy is designed to scale from the current single-account setup to a future multi-account AWS Organizations structure, where billing-mode and client map directly to account-level separation.


Decision

All AWS resources provisioned by Ontopix MUST carry a standard set of tags. Tags are organized in two tiers: Required (applied to every resource without exception) and Contextual (applied based on billing-mode). A third, optional tier exists for fine-grained operational signals.

Tags are defined in Terraform locals and injected via default_tags on the AWS provider. Manual exceptions must be documented and tagged before reaching production.


Tag Taxonomy

Tier 1 — Required

These tags MUST be present on every AWS resource.

Tag keyPurposeValues
productOntopix product line that owns this resourceagents · audits · mistery · platform · shared
billing-modeWho bears the cost of this resourceclient · saas · internal · rd
envDeployment environmentprod · pre · dev · sandbox
teamTeam accountable for this resourceengineering · marketing · operations · infra · finance
ownerEmail address of the accountable individual or team aliasplatform@ontopix.ai · engineering@ontopix.ai
sourceRepository and path of the Terraform root module that created this resourceontopix/infra/global · ontopix/mcp-services/examples/hello-world/.infra
managed-byTool used to provision this resourceterraform · manual · cdk

billing-mode semantics

ValueMeaning
clientCost is attributable to a specific client and may be recovered through invoicing
saasCost belongs to the shared SaaS platform, not a specific client
internalOntopix internal tooling — not tied to any client or product line
rdResearch and development; no current billing target

env values

ValueMeaning
prodProduction — live, customer-facing
prePre-production / staging — mirrors production
devDevelopment — active development and integration testing
sandboxIsolated developer sandbox; ephemeral, non-shared

source convention

Format: ontopix/{repository-name}/{path-to-root-module}

The path is the directory containing the Terraform root module (i.e. the directory with backend.tf). This is a static string — it is not derived from a variable. It is the only tag that requires a manual override when a repository has multiple root modules (e.g. per-environment .infra/ directories).

ontopix/infra/global
ontopix/schemas/.infra
ontopix/mcp-services/examples/hello-world/.infra

owner convention

Use team aliases for shared or long-lived infrastructure (platform@ontopix.ai, engineering@ontopix.ai). Use personal addresses only for short-lived or individually owned resources. Personal addresses must be updated when ownership changes.


Tier 2 — Contextual

These tags are required when billing-mode = client. They provide the detail needed to attribute costs to a specific client engagement and to produce accurate invoices.

Tag keyPurposeValues / format
clientClient identifierLowercase single word or acronym (e.g. acme, retailco)
projectSpecific engagement within the client relationshipShort slug (e.g. support-agent-v1, audit-pilot)
cost-centerFinance cost center ID for billing roll-upProvided by Finance (see open questions)
componentArchitectural layer this resource belongs toapi · worker · db · infra · ml · frontend · edge

Notes on Tier 2 values

client: No canonical client registry exists yet. Use lowercase, no hyphens, no spaces. A client registry will be established as a follow-on to this ADR.

project: Only used when billing-mode = client. Not used for saas, internal, or rd resources.

cost-center: The specific taxonomy and IDs will be defined by Finance. This tag MUST be set on all billing-mode = client resources; the value will be provided per engagement. Placeholder: tbd until Finance publishes the registry.


Tier 3 — Optional (Extended attributes)

Optional tags for fine-grained tracking. All Tier 3 tag keys use the prefix extra: to distinguish them from required tags and to prevent key collisions with future Tier 1/2 additions.

Tag keyPurposeValue format
extra:featureSpecific product feature or architectural experimentdebounce · copilot
extra:channelCommunication channel served by this resourcewhatsapp · email · zendesk
extra:data-classData sensitivity classificationpii · internal · public

The extra: prefix convention means:

  • Tier 3 tags are immediately recognizable in Cost Explorer filters and AWS Config.
  • Adding a new Tier 3 tag never risks colliding with a future required tag name.
  • extra:* can be excluded or included as a group in IAM tag conditions and Config rules.

Implementation

Terraform — provider-level default tags

# .infra/providers.tf

provider "aws" {
  region = var.aws_region

  default_tags {
    tags = local.tags
  }
}

Terraform — locals pattern

# .infra/locals.tf

locals {
  tags = {
    # Tier 1 — always required
    product      = "agents"
    billing-mode = "client"
    env          = var.environment
    team         = "engineering"
    owner        = "engineering@ontopix.ai"
    source       = "ontopix/my-repo/.infra"
    managed-by   = "terraform"

    # Tier 2 — required because billing-mode = "client"
    client       = "acme"
    project      = "support-agent-v1"
    cost-center  = "tbd"          # replace with Finance-issued ID
    component    = "worker"

    # Tier 3 — optional
    "extra:channel" = "channel-whatsapp"
  }
}

Environment variable validation

# .infra/variables.tf

variable "environment" {
  description = "Deployment environment"
  type        = string
  validation {
    condition     = contains(["prod", "pre", "dev", "sandbox"], var.environment)
    error_message = "environment must be one of: prod, pre, dev, sandbox"
  }
}

AWS Config enforcement

A Config managed rule in the central infra repository enforces Tier 1 tags on all resources:

resource "aws_config_config_rule" "required_tags" {
  name = "ontopix-required-tags"

  source {
    owner             = "AWS"
    source_identifier = "REQUIRED_TAGS"
  }

  input_parameters = jsonencode({
    tag1Key = "product"
    tag2Key = "billing-mode"
    tag3Key = "env"
    tag4Key = "team"
    tag5Key = "owner"
    tag6Key = "source"
    # tag7Key = "managed-by"  # enable after remediation window
  })
}

Enforcement rollout

Enforcement follows a two-phase approach:

  1. Phase 1 — Alerting only (first 30 days): Enable the REQUIRED_TAGS Config rule in evaluation mode. Non-compliant resources trigger alerts routed to the resource owner (when available) and to infra@ontopix.ai as a catch-all. Alert channels: email notification and, where feasible, a GitHub issue on the repository identified by the source tag.
  2. Phase 2 — Block non-compliant creates: After the team has built the habit of reacting to alerts and existing resources have been remediated, enforce compliance via SCP to prevent creation of resources missing Tier 1 tags.

Alternatives Considered

Account-per-client isolation via AWS Organizations

Separate accounts per client provides hard billing and security boundaries. This is the correct long-term architecture and this taxonomy is explicitly designed to translate into it: billing-mode=client + client=X maps to a future client-X account. Rejected as the immediate solution due to operational overhead at current scale.

Tag keys in PascalCase (ManagedBy, Environment)

Consistent with some AWS-native tools and third-party FinOps platforms. Rejected because our Terraform and YAML conventions use kebab-case throughout, and mixed casing across tag keys breaks Cost Explorer filter composition silently (tag keys are case-sensitive in AWS).

No extra: prefix for optional tags

Simpler, but creates ambiguity between an established required tag and a locally adopted optional one. The prefix makes tier membership visible without consulting this document.

Free-form cost-center values

Letting teams define their own cost center slugs produces unsquashable inconsistency. Deferring to Finance for a formal registry is the right call even if it means a short tbd period.


Consequences

Positive

  • Per-client, per-product, and per-team cost reports are immediately constructable in AWS Cost Explorer using Tier 1 tags.
  • source enables one-click navigation from any tagged AWS resource to the exact Terraform module that created it.
  • owner enables cost anomaly alerts and security findings to route to the right contact without manual lookup.
  • The taxonomy is future-proof: Tier 1 + Tier 2 tags map directly to AWS Organizations account structure when multi-account separation is adopted.
  • AI agents provisioning infrastructure have an explicit, checkable contract.

Negative / Trade-offs

  • source cannot be set purely via default_tags in repositories with multiple Terraform root modules — requires a per-module local override.
  • cost-center will carry tbd values until Finance publishes the cost center registry. This limits invoice reconciliation accuracy in the short term.
  • Existing untagged resources require a one-time remediation pass before Config enforcement is enabled.
  • owner values on long-lived resources need active governance; team aliases are strongly preferred over personal addresses for shared infrastructure.
  • billing-mode=rd can accumulate spend silently if not reviewed regularly. Engineering leadership should review rd-tagged costs monthly.

Open Questions (RFC)

  1. Finance cost center registry — when will Finance define the cost-center taxonomy? Until then, what is the approved placeholder value? (tbd, unassigned, or per-engagement provisional slugs?)
  2. Client registryclient values are currently informal (lowercase slug, no source of truth). Should a canonical client registry be maintained as part of this ADR, in a separate document, or deferred to a CRM integration?
  3. extra: tag governance — who approves new extra: tag keys to prevent proliferation? Engineering lead sign-off, or self-service with documentation required?

Resolved Questions

  1. Config enforcement rollout — alerting-only for 30 days, then block non-compliant resources via SCP. Alerts route to the resource owner and to infra@ontopix.ai. Where feasible, create a GitHub issue on the repository identified by the source tag.
  2. Multi-account transitionbilling-mode is retained even after adopting AWS Organizations. Not every client will warrant a dedicated account; only the larger engagements will. Keeping billing-mode on all resources regardless of account provides consistent cost tracking across the entire estate.

Applies Principles

  • Evidence Over Assumptions — tagged resources produce cost data; untagged resources require guesswork.
  • Ownership & Responsibilityowner and team make accountability visible in billing reports, security findings, and incident response.
  • Automation Over Manual Workdefault_tags on the provider means tagging is a one-time definition, not per-resource boilerplate.
  • Long-Term Thinking — the taxonomy translates directly to AWS Organizations account-level separation when Ontopix reaches that scale.
  • Security by Designmanaged-by=manual is a drift signal; owner routes security findings to the right contact without manual lookup.