CF Convention

June 5, 2026 · View on GitHub

Description

CF semantic metadata — standard names, units, axis, calendar, and data-quality attributes — for Zarr data variables and coordinates.

This convention attaches meaning to any data variable or coordinate. It is the semantic layer that the coords convention explicitly defers to: where coords says where a coordinate's values live, cf says what it is (its standard name, units, axis role, calendar, valid range, flags, …). It is a Zarr-native, namespaced expression of the descriptive attributes defined by the CF (Climate and Forecast) Metadata Conventions, sections 3 and 4.1–4.4.

cf is one of a planned set of focused CF-related Zarr conventions and covers only the semantic attributes part. Other CF concerns are intended for sibling conventions (e.g. cell bounds & methods, parametric vertical coordinates); see CF-to-zarr.md for the full split.

All properties use the cf: namespace prefix and are placed at the root attributes level following the Zarr Conventions Specification.

Where the metadata lives

The CF metadata is always the same object — a CF attribute descriptor (standard_name, units, axis, …). What changes is where that descriptor is attached. There are three placements, integrated with the coords convention; a node MUST carry at least one of them:

#PropertyLives onDescribesCF source
1cf:attributesan arraythat array's own variableNetCDF — attrs on the variable
2cf:attributes embedded in coords:coordinates[name]the node declaring coordsone coordinateNetCDF — attrs on the coordinate variable
3cf:variablesa groupthe group's child data variablesZarr-native convenience (no CF equivalent)

Placements 1 and 2 are both the cf:attributes descriptor — applied to an array, or embedded in a coordinate's coords:coordinates entry; both are specified under cf:attributes below, including how cf extends a coordinate and how readers resolve a type: "array" coordinate. Placement 3 is specified under cf:variables. Worked snippets of all three are in Examples.

// group (…/dataset)
"cf:variables": {
  "sst": { "standard_name": "sea_water_temperature", "units": "K" },
  "sss": { "standard_name": "sea_water_salinity", "units": "1e-3" }
}

Motivation

  • Provides standardized cf metadata for Zarr datasets — a uniform way to carry CF descriptive vocabulary (standard_name, units, axis, calendar, flags) without re-implementing coordinate location or CRS.
  • Composable with other Zarr conventions (e.g., coords, spatial, proj, multiscales).
  • Uses integer-major + URL pin versioning: the schema_url carries the major (/refs/tags/v1/schema.json); all v1.x changes are additive.

Composes with

  • coordscoords locates a coordinate (which array / inline values / interval); cf gives it meaning. A coordinate's cf:attributes is embedded inside its coords:coordinates descriptor, so location and meaning are declared together.
  • proj / spatial — supply the CRS and the affine transform for spatial axes; cf adds their descriptive attributes. Orthogonal, applied on the same node.
  • multiscales — each level array can independently carry its own cf:attributes.

Out of scope (delegated)

cf is deliberately limited to semantics. It does not locate coordinate values (→ coords), define a CRS (→ proj), express an affine transform (→ spatial), describe cell extent / bounds / methods (→ a future cf-cells convention), formulate parametric vertical axes (→ a future cf-vertical convention), or model feature collections / Discrete Sampling Geometries (→ an extension of coords). See CF-to-zarr.md for the full split.

Convention Registration

The convention must be registered in zarr_conventions:

{
  "zarr_conventions": [
    {
      "schema_url": "https://raw.githubusercontent.com/zarr-conventions/cf/refs/tags/v1/schema.json",
      "spec_url": "https://github.com/zarr-conventions/cf/blob/v1/README.md",
      "uuid": "0c0a02d2-8a95-4303-8b62-16a50b439d74",
      "name": "cf",
      "description": "CF semantic metadata — standard names, units, axis, calendar, and data-quality attributes — for Zarr data variables and coordinates."
    }
  ]
}

Applicable To

This convention can be used with these parts of the Zarr hierarchy:

  • Group
  • Array

On an array, cf:attributes describes the array's own variable (a data variable, or a type: "array" coordinate array). On a group, cf:variables is an optional catalogue describing the group's child data-variable arrays. Coordinate metadata, on either node type, is embedded inside the relevant coords:coordinates descriptor.

Properties

All properties use the cf: namespace prefix and are placed at the root attributes level. A node MUST carry at least one of cf:attributes, coords:coordinates (with embedded cf:attributes), or cf:variables.

Field NameTypeRequiredDescription
cf:attributesobjectConditional*The CF attribute descriptor for this array's own variable, on a data-variable array or a type: "array" coordinate array.
cf:variablesobjectConditional*(Group only) Optional map from a data-variable name to a CF attribute descriptor — a convenience catalogue for a group's child arrays.
cf:versionintegerOptionalMajor version pin (currently 1). Optional because schema_url already pins the major.

Coordinate descriptors additionally carry an embedded cf:attributes inside each coords:coordinates entry — see Where the metadata lives.

* A node MUST provide at least one of cf:attributes, an embedded cf:attributes in coords:coordinates, or cf:variables.

Additional Properties

Additional properties are allowed.

CF attribute descriptor

The shared object carried by every placement below: a set of CF attributes for a single variable or coordinate. Every field is optional — different variables use different subsets — and the descriptor is additionalProperties: true, so any CF attribute not listed here passes through unchanged. The Applies to column notes whether a field is meaningful on a coordinate, a data variable, or both; the schema does not enforce it (a field that does not apply is simply omitted).

FieldTypeApplies toDescription
standard_namestringbothName from the CF standard name table identifying the physical quantity.
long_namestringbothHuman-readable descriptive name.
unitsstringbothUDUNITS units. For time, the <unit> since <epoch> form (e.g. days since 1850-01-01).
commentstringbothMiscellaneous information about the variable.
referencesstringbothReferences describing the variable or its production.
sourcestringdata variableMethod of production of the original data.
axisstringcoordinateAxis role for a coordinate: X, Y, Z, or T.
positivestringcoordinateDirection of increasing values for a vertical coordinate: up or down.
calendarstringcoordinateCalendar for a time coordinate (standard, proleptic_gregorian, noleap, 360_day, …).
flag_valuesarraydata variableValues corresponding to flag_meanings.
flag_masksarraydata variableBit masks corresponding to flag_meanings.
flag_meaningsstringdata variableSpace-separated flag names matching flag_values / flag_masks.
valid_minnumberbothSmallest valid value.
valid_maxnumberbothLargest valid value.
valid_rangenumber[2]both[min, max] valid range.
ancillary_variablesstringdata variableSpace-separated names of ancillary variables.

This descriptor appears in three placements — cf:attributes, embedded in coords:coordinates, and cf:variables — specified next.

cf:attributes

A single CF attribute descriptor describing one variable. It is used in two of the three placements (1 and 2).

On an array — placement 1

A Zarr array is a single variable, so a top-level cf:attributes describes that array's variable — the same place NetCDF keeps a variable's attributes. Use it on a data-variable array, or on an explicit coordinate array (the sibling array a coords type: "array" descriptor points at). A reader takes attributes["cf:attributes"] as the CF metadata of the variable the array represents.

Embedded in a coordinate — placement 2

The primary way to describe coordinates: the descriptor is added inside the coordinate's coords:coordinates entry, under a cf:attributes key. Because coords descriptors are additionalProperties: true, cf extends each one in place without changing the coords schema. The shape inside a node's attributes:

attributes
└─ coords:coordinates                    ← map (owned by the coords convention)
   └─ <coordinate-name>                   ← one coordinate descriptor
      ├─ type        "array" | "inline" | "interval" | "reference"  ┐ location
      ├─ path | values | start/end/step | convention                ┘ (owned by coords)
      └─ cf:attributes                    ← added by THIS convention
         ├─ standard_name
         ├─ units
         ├─ axis / positive / calendar
         └─ …                             (a CF attribute descriptor)

So one object declares both halves of a coordinate — coords says where it is, the embedded cf:attributes says what it means. It works for every coords descriptor type, and for inline, interval, and reference coordinates (which have no array of their own) it is the only place their CF metadata can live. Validation splits cleanly: coords validates the location fields, cf validates the embedded cf:attributes.

Resolving a type: "array" coordinate

A type: "array" coordinate MAY instead carry its descriptor on the target coordinate array itself (placement 1); its coords:coordinates entry then simply omits cf:attributes. A reader resolves the coordinate's CF metadata as:

  1. the cf:attributes embedded in its coords:coordinates descriptor, if present; otherwise
  2. the top-level cf:attributes on the array at path.

Writers SHOULD use one location, not both; if both appear they SHOULD be consistent and the embedded descriptor — declared with the coordinate's actual use — is authoritative.

See Examples for a worked snippet of each placement.

cf:variables

Group only; optional (placement 3). A map from a data-variable name to a CF attribute descriptor, declared once on a group as a convenience catalogue for its child data-variable arrays (handy with consolidated metadata).

  • Type: object (map)
  • Keys: a data-variable name.
  • Values: a CF attribute descriptor.
  • Scope: a Zarr-native convenience, not a NetCDF-CF construct. Not meaningful on an array (an array is one variable — use cf:attributes), and not for coordinates (use the embedded form, placement 2).
  • Overlap: it overlaps placement 1 (both describe a data variable); the two SHOULD be consistent, and a variable's own array cf:attributes is authoritative if both are present.

cf:version

Optional integer pinning the major version of the convention this metadata was authored against. Currently 1. Omitting it is fine — schema_url already pins the major.

Examples

Three end-to-end usage patterns follow, each as a minimal snippet; see the examples directory for the complete, validated metadata documents.

Approach 1 — self-describing coordinate array

A coordinate array carrying its own cf:attributes (placement 1 applied to a coordinate array) — the array a coords type: "array" descriptor points at, when its coords:coordinates entry omits the embedded form. Full file: examples/cf-coordinate-array.json.

{
  "zarr_format": 3,
  "node_type": "array",
  "dimension_names": ["time"],
  "attributes": {
    "zarr_conventions": [
      { "name": "cf", "schema_url": "https://raw.githubusercontent.com/zarr-conventions/cf/refs/tags/v1/schema.json" }
    ],
    "cf:attributes": { "standard_name": "time", "units": "days since 1850-01-01", "calendar": "noleap", "axis": "T" }
  }
}

Approach 2 — group catalogue of data variables

An optional cf:variables map on a group (placement 3). Full file: examples/cf-group-catalogue.json.

{
  "zarr_format": 3,
  "node_type": "group",
  "attributes": {
    "zarr_conventions": [
      { "name": "cf", "schema_url": "https://raw.githubusercontent.com/zarr-conventions/cf/refs/tags/v1/schema.json" }
    ],
    "cf:variables": {
      "sst": { "standard_name": "sea_water_temperature", "units": "K" },
      "sss": { "standard_name": "sea_water_salinity", "units": "1e-3" }
    }
  }
}

Approach 3 — data variable with coordinates

The comprehensive case (uses placements 1 and 2): a data-variable array describes itself via a top-level cf:attributes, and each of its coordinates carries an embedded cf:attributes inside its coords:coordinates descriptor. Full file: examples/cf.json.

{
  "zarr_format": 3,
  "node_type": "array",
  "dimension_names": ["time", "x"],
  "attributes": {
    "zarr_conventions": [
      { "name": "cf",     "schema_url": "https://raw.githubusercontent.com/zarr-conventions/cf/refs/tags/v1/schema.json" },
      { "name": "coords", "schema_url": "https://raw.githubusercontent.com/zarr-conventions/coords/refs/tags/v1/schema.json" }
    ],
    "coords:coordinates": {
      "time": {
        "type": "array", "path": "../time",
        "cf:attributes": { "standard_name": "time", "units": "days since 1850-01-01", "calendar": "noleap", "axis": "T" }
      },
      "x": {
        "type": "reference", "convention": "spatial",
        "cf:attributes": { "standard_name": "projection_x_coordinate", "units": "m", "axis": "X" }
      }
    },
    "cf:attributes": { "standard_name": "sea_water_temperature", "units": "K" }
  }
}

Versioning and Compatibility

This convention follows the integer-major with URL pin contract (contract #4 in the Zarr Conventions Guidance Implementation Contracts):

  • The schema_url and spec_url carry the integer major version (/refs/tags/v1/..., /blob/v1/...).
  • All v1.x changes are additive: new optional fields and broadened ranges only.
  • Breaking changes (renaming, retyping, removing, or semantically shifting existing fields) require a new major: tag v2 and publish a fresh schema under /refs/tags/v2/schema.json.
  • Readers SHOULD tolerate unknown additional fields per the conventions framework's safely-ignorable principle.

Acknowledgements

The attribute vocabulary is drawn from the CF (Climate and Forecast) Metadata Conventions. This convention carries the CF semantics layer that the coords convention defers to.