Open Climate Service Managed Data Guide¶
This guide describes the current native FastAPI surface for Open Climate Service and how datasets are published as STAC collections.
The current public story is:
- run and inspect ingestion operations with
/ingestions - discover the configured extent with
/extent - discover managed datasets with
/datasets - discover vector feature collections with
/features - discover published GeoZarr datasets with
/stac/catalog.json - access raw Zarr data with
/zarr/{dataset_id}(vanilla zarr clients, web maps) - access the native Icechunk store with
/icechunk/{dataset_id}(Icechunk SDK) - preview a dataset with
/datasets/{dataset_id}/thumbnail.png(also a STAC collection asset)
Internal artifacts still exist as a storage and provenance model, but they are not part of the public API contract.
Operational note:
/ingestionsis the execution and admin-facing surface for ingestion runs/datasetsis the canonical managed-data surface for consumers
Main Public Endpoints¶
POST /ingestionsGET /ingestionsGET /ingestions/{ingestion_id}GET /extentGET /datasetsGET /datasets/{dataset_id}GET /datasets/{dataset_id}/downloadGET /datasets/{dataset_id}/thumbnail.pngGET /featuresGET /features/{collection_id}GET /features/{collection_id}/data.parquetGET /stacGET /stac/catalog.jsonGET /stac/collections/{dataset_id}GET /zarr/{dataset_id}GET /zarr/{dataset_id}/{relative_path}GET /icechunk/{dataset_id}/{path}GET /sync/{dataset_id}/planPOST /sync/{dataset_id}
1. Discover the configured extent¶
The configured extent is setup-time Open Climate Service configuration. It is read-only at runtime.
Example:
Example response:
What this means:
bboxis the resolved spatial extent exposed publicly- provider-specific hints may exist internally in extent config, but they are not part of the public extent response
2. Ingest a dataset¶
The public ingestion contract takes:
dataset_idstart- optional
end overwritepublish
Raw bbox and country_code are not part of the public ingestion payload — the API resolves them from the configured extent. All datasets are stored as Icechunk stores; there is no format selection parameter.
Example: CHIRPS3¶
curl -s -X POST http://127.0.0.1:9000/ingestions \
-H "Content-Type: application/json" \
-d '{
"dataset_id": "chirps3_precipitation_daily",
"start": "2024-01-01",
"end": "2024-01-31",
"overwrite": false,
"publish": true
}' | jq
Example: WorldPop¶
curl -s -X POST http://127.0.0.1:9000/ingestions \
-H "Content-Type: application/json" \
-d '{
"dataset_id": "worldpop_population_global2_100m",
"start": "2020",
"end": "2020",
"overwrite": false,
"publish": true
}' | jq
Example response:
{
"ingestion_id": "a7e06c93-ba78-4c74-b772-160927fdb463",
"status": "completed",
"dataset": {
"dataset_id": "chirps3_precipitation_daily",
"source_dataset_id": "chirps3_precipitation_daily",
"dataset_name": "Total precipitation (CHIRPS3)",
"short_name": "Total precipitation",
"description": "CHIRPS v3 daily precipitation in mm.",
"itemType": "coverage",
"variable": "precip",
"period_type": "daily",
"units": "mm",
"resolution": "5 km x 5 km",
"source": "CHIRPS v3",
"source_url": "https://www.chc.ucsb.edu/data/chirps3",
"extent": {
"spatial": {
"xmin": -13.52499751932919,
"ymin": 6.92499920912087,
"xmax": -10.124997468665242,
"ymax": 10.02499925531447
},
"temporal": {
"start": "2024-01-01",
"end": "2024-01-31"
}
},
"last_updated": "2026-04-01T09:03:28.691120Z",
"links": [
{
"href": "/datasets/chirps3_precipitation_daily",
"rel": "self",
"title": "Dataset detail"
},
{
"href": "/zarr/chirps3_precipitation_daily",
"rel": "zarr",
"title": "Zarr store"
},
{
"href": "/stac/collections/chirps3_precipitation_daily",
"rel": "stac",
"title": "STAC collection"
}
],
"publication": {
"status": "published",
"published_at": "2026-04-01T09:03:28.692230Z"
}
}
}
What this means:
ingestion_idis the handle for the ingestion event lookup routestatus = "completed"means this branch still treats ingestion synchronously/ingestionsis an operational/admin surface, not the main managed-data catalogdatasetis a public managed dataset summary, not an internal artifact recordextentis realized data coverage, not just the configured bboxlinkspoint to the native dataset metadata, native Zarr access, and STAC collection metadata
3. List ingestion runs¶
GET /ingestions returns ingestion run records for operational and admin use.
Example:
What this means:
- this route is for execution lookup and operational visibility
- it is not intended to replace
/datasetsas the primary data discovery surface - items are ordered from most recent ingestion to oldest
4. Ingestion failure behavior¶
Ingestion should fail gracefully with a structured API error, not a raw 500 stack trace.
Current behavior:
- invalid or missing spatial/config inputs return
400 - dataset/provider execution failures return
502
Example cases:
- a provider requires a country code and the resolved extent config does not provide one
- a dataset requires a bbox and no bbox can be resolved
- the upstream provider fails at download time
Example error response:
5. Discover managed datasets¶
GET /datasets is the native managed-data catalog and the main consumer-facing data surface.
Example:
Example response:
{
"kind": "DatasetList",
"items": [
{
"dataset_id": "chirps3_precipitation_daily",
"source_dataset_id": "chirps3_precipitation_daily",
"dataset_name": "Total precipitation (CHIRPS3)",
"short_name": "Total precipitation",
"description": "CHIRPS v3 daily precipitation in mm.",
"itemType": "coverage",
"variable": "precip",
"period_type": "daily",
"units": "mm",
"resolution": "5 km x 5 km",
"source": "CHIRPS v3",
"source_url": "https://www.chc.ucsb.edu/data/chirps3",
"extent": {
"spatial": {
"xmin": -13.52499751932919,
"ymin": 6.92499920912087,
"xmax": -10.124997468665242,
"ymax": 10.02499925531447
},
"temporal": {
"start": "2024-01-01",
"end": "2024-01-31"
}
},
"last_updated": "2026-04-01T09:03:28.691120Z",
"links": [
{
"href": "/datasets/chirps3_precipitation_daily",
"rel": "self",
"title": "Dataset detail"
},
{
"href": "/zarr/chirps3_precipitation_daily",
"rel": "zarr",
"title": "Zarr store"
}
],
"publication": {
"status": "published",
"published_at": "2026-04-01T09:03:28.692230Z"
}
}
]
}
What this means:
/datasetsis the public native catalog of managed datasetsitemTypesays what the dataset holds:coveragefor a raster,featurefor a feature collection such as a boundary set. It is the field to filter a listing on, becauseformatsits on the nested version record and onlyGET /datasets/{dataset_id}returns those. The name and thefeaturevalue come from OGC API - Features Part 1, which definesitemTypeon the collection object;coverageis convention rather than conformance, since OGC API - Coverages is a candidate draft and silent on the field./datasetsis Open Climate Service's own API, so do not infer the rest of an OGC collection object from the borrowed name.variableandperiod_typeare both nullable, and arenulltogether for a feature collection: a boundary set measures nothing and has no temporal axis. ReaditemTyperather than testing these two for absence.-
licenseis an SPDX identifier, orotherfor a licence that has none — the Copernicus licence, for instance. It is never absent: a dataset whose template declares no licence reportsotherrather than something that reads as permissive.license_urlpoints at the licence text where one is known, and the STAC collection carries the same information as arel: licenselink plusprovidersfor attribution. -
descriptioncarries the dataset template's own prose, and isnullwhen the template declares none. It is where a dataset states what its values actually mean, so it is worth reading before using one:chirps3_precipitation_monthlyis a mean daily rate rather than a monthly total. The same text is published as the STAC collectiondescription. itemsis wrapped in akindenvelope for consistency and self-description- dataset items contain public metadata and access links only
- internal artifact ids, filesystem paths, and downloader implementation details are intentionally omitted
6. Get dataset detail¶
GET /datasets/{dataset_id} returns the full managed dataset detail view.
Example:
What this adds beyond the list response:
- full dataset metadata
- publication summary
- slim
versionshistory derived from internal records
The detailed dataset response is where version history belongs. The ingestion response stays as a summary.
7. Access raw Zarr data¶
/zarr/{dataset_id} bridges the Icechunk store into standard zarr HTTP semantics. It works with any vanilla zarr client including xarray.
Examples:
/zarr/{dataset_id} is the store prefix; the root metadata is at /zarr/{dataset_id}/zarr.json.
8. Access the Icechunk store natively¶
/icechunk/{dataset_id}/{path} serves raw Icechunk store files for native SDK access. Use this when you need versioning or want to avoid the zarr proxy layer.
import icechunk, xarray as xr
repo = icechunk.Repository.open(icechunk.http_storage("http://127.0.0.1:9000/icechunk/chirps3_precipitation_daily"))
ds = xr.open_zarr(repo.readonly_session("main").store, zarr_format=3, consolidated=False)
Both endpoints are advertised as assets in the STAC collection:
"assets": {
"zarr": {
"href": "https://host/zarr/chirps3_precipitation_daily",
"type": "application/vnd.zarr; version=3",
"xarray:open_kwargs": { "consolidated": true }
},
"icechunk": {
"href": "https://host/icechunk/chirps3_precipitation_daily",
"type": "application/octet-stream",
"xarray:open_kwargs": { "zarr_format": 3, "consolidated": false }
},
"thumbnail": {
"href": "https://host/datasets/chirps3_precipitation_daily/thumbnail.png",
"type": "image/png",
"title": "Thumbnail",
"roles": ["thumbnail"]
}
}
The thumbnail asset is present only when the image exists — see section 9.
A pyramided store advertises one extra media type parameter:
This is how a client learns the store has resolution levels — the plain Zarr media type cannot
express it, and clients that only render pyramided stores (STAC Browser, via
ol/source/GeoZarr) will not offer to display a store without it. The parameter is only added
when the store genuinely has a non-empty multiscales.layout; claiming it for a flat store
would send a renderer looking for levels that do not exist.
It is matched as a literal, not parsed, so the string is byte-for-byte fixed: same parameter
order, one space after each ;. Reformatting it silently disables rendering.
9. Fetch a dataset thumbnail¶
GET /datasets/{dataset_id}/thumbnail.png serves a small PNG preview of the dataset: one
representative 2-D slice, styled with the template's display.colormap, longest side 512 px,
missing values transparent.
A thumbnail is written at the end of each ingest and sync run, so a published dataset normally
has one. The endpoint 404s only when none has ever been produced — a dataset not yet ingested,
or a first render that failed or found nothing to draw. A later run that fails, or whose chosen
slice is entirely missing, leaves the previous image in place rather than deleting it, so a
served thumbnail can be a run or more stale. The STAC collection advertises the thumbnail
asset only when the image exists.
10. Access published STAC collections¶
Published Zarr-backed datasets are exposed through /stac for discovery.
STAC examples:
curl -s "http://127.0.0.1:9000/stac/catalog.json" | jq
curl -s "http://127.0.0.1:9000/stac/collections/chirps3_precipitation_daily" | jq
What this means:
/stacis the public STAC discovery surface for published datasets, raster and vector alike- native FastAPI no longer exposes
/collections - dataset responses include
/stac/collections/{dataset_id}
Raster and feature collections are described differently¶
One catalogue, two representations. STAC has no itemType — a Collection's type is always
"Collection" — so a client tells them apart by the extensions and asset media types they
declare:
| Raster | Feature collection | |
|---|---|---|
| Extensions | datacube, zarr, projection |
table, projection |
| Describes the data with | cube:dimensions, cube:variables |
table:row_count, table:primary_geometry, table:columns |
| Data asset | zarr (plus icechunk) |
data, application/x-parquet |
A feature collection emits no cube: fields and no Zarr asset, and a raster emits no table:
fields. Both carry the same envelope: licence, rel: license link when the licence is a URL
rather than an SPDX identifier, providers for attribution, and the self/root/parent links.
Two things worth knowing about a feature collection's document:
table:primary_geometrynames a column, not a geometry type. One column can hold points and polygons together, which is what an org unit hierarchy looks like: polygons at the upper levels, facility points at the lower ones.- It declares no temporal extent (
[[null, null]]). Static geometry has no time axis, and a release identifier such as2026-08-19.0is not an instant — turning one into a temporal extent would publish a range no feature was observed in.
11. Discover feature collections¶
GET /features is the inventory of the vector collections this instance holds — org unit
boundaries and facility points, stored as GeoParquet. They also appear under /datasets with
itemType: "feature"; /features is where the vector-specific facts live.
Example response:
{
"kind": "FeatureCollectionList",
"items": [
{
"id": "districts",
"name": "District boundaries",
"description": "District boundaries from the national hierarchy.",
"license": "CC-BY-4.0",
"license_url": "https://creativecommons.org/licenses/by/4.0/",
"attribution": "Ministry of Health",
"id_property": "orgUnitCode",
"feature_count": 202,
"geometry_types": ["Polygon"],
"primary_geometry": "geometry",
"crs": "EPSG:4326",
"version": null,
"extent": {
"spatial": {
"xmin": -13.5,
"ymin": 6.9,
"xmax": -10.1,
"ymax": 10.0
},
"temporal": { "start": null, "end": null }
},
"last_updated": "2026-09-21T10:14:02.118330Z"
}
]
}
What this means:
- A record is what makes a collection exist. The listing reads records, never the filesystem, so a GeoParquet file placed in the store directory by hand does not appear. The store directory is not an inbox, and there is no reconciliation step in which disk and index can disagree.
id_propertynames the property each feature is identified by. That value becomes the location column of a DHIS2 or CHAP export, so it must identify exactly one feature — a duplicate is not a dropped feature, it is two features pushing values against one org unit.crsis the CRS the geometry is actually stored in, and is never assumed. A collection in a projected CRS reports its own extent underextent.spatialand the WGS 84 one underextent.spatial_wgs84, the same convention a raster in a projected CRS uses.geometry_typesis read from the stored file's own metadata. An empty list means the file declares none, which is "not stated" rather than "no geometry".description,licenseandattributioncome from the collection's template. Until feature templates land (CLIM-926) they read asnullandother—licenseis never absent, and an undeclared licence reportsotherrather than something that reads as permissive.- Unpublished collections are listed.
/featuresreports what this instance holds; publication decides what the catalogues advertise, which is a different question.
Reads of the geometry itself are windowed by a bounding box, and an unwindowed read of a large collection is refused rather than served by accident — a national hierarchy runs to the thousands of features, and pulling all of it should be deliberate.
GET /features/{collection_id}/data.parquet serves the stored GeoParquet for a published
collection, as application/x-parquet. It is the href the collection's STAC data asset
advertises, and it resolves through the registered record — an unregistered file in the store
directory is not reachable through it.
12. /sync¶
/sync advances an existing managed dataset from its latest local coverage toward a requested upstream period.
Available operations:
GET /sync/{dataset_id}/plan?end={period}returns the planned sync action without downloading or writing dataPOST /sync/{dataset_id}executes the plan when a new version is needed
Implemented behavior:
temporaldatasets compare the next missing period with the requested or metadata-clamped latest periodreleasedatasets compare the current materialized release with the requested or metadata-clamped latest releasestaticdatasets returnnot_syncable- preserve stable managed dataset identity
- use template-level
sync_execution appendexecution reuses the sync planner's source-available delta and writes only periods missing from the committed storerematerializeexecution downloads the complete planned contiguous union into a sibling store and publishes it only after validation- return the updated dataset view plus structured
sync_detail - validate realized temporal coverage against the planned contiguous artifact scope; retain the caller's request scope separately as provenance
Current sync constraints:
- append execution is a delta-download plus canonical rebuild, not in-place Zarr mutation
- upstream availability is determined by each plugin's
periods()method
Release identity¶
A release dataset's template may declare a release identity, independent of period_type
and of temporal coverage. It has two halves:
sync:
kind: release
version:
value: R2025A # the identifier, verbatim as the source publishes it
authority: worldpop # whose versioning scheme gives that identifier meaning
The pair is the identity — a bare R2025A or 1.0 says nothing on its own, so both halves
are required and neither is inferred. value is opaque: OCS never parses, orders or
normalises it, because its syntax belongs to the authority. Because it is stored exactly as
declared, a blank or whitespace-padded value is rejected rather than trimmed — otherwise
" R2025A " would be a different release from R2025A. authority is a stable machine
identifier (worldpop, overture, ocs), never a display label — display names live on
source and providers — and it is compared exactly, so changing it renames every release
under it. Both are capped at 64 characters, and the same rules apply wherever an identity is
built: a template at registration, an artifact at materialization, a record on load.
That identity is stored on the materialized artifact and reported on every release plan:
{
"current_version": { "value": "R2025A", "authority": "worldpop" },
"target_version": { "value": "R2025B", "authority": "worldpop" }
}
sync_detail.current_version— the release the local artifact holdssync_detail.target_version— the release the template currently declares
It exists because a source can republish the same periods under a new revision, which no
period comparison can detect. When the two differ, sync rematerializes even though temporal
coverage is unchanged — and they are compared as a whole, so the same value under a
different authority is a different release. An artifact materialized before its template
declared a version has current_version: null; its release is unknown rather than
known-equal, so it rematerializes once to establish identity and then settles.
A version is a logical release, distinct from the other two identities on a record:
artifact_id is the exact materialization, and provenance is how it was produced. A derived
dataset does not inherit a version from its inputs — an openEO result carries no version
unless OCS deliberately releases it, as {"value": "1.0", "authority": "ocs"}, with its
inputs recorded as provenance.
A template that declares no version keeps the period-based behaviour described above, and a
temporal dataset never carries a release identity however many periods it appends.
Rematerializing for a new release never shortens a managed dataset. If the declared release cannot cover what is already held — the source reports nothing, stops short of the current end, or no longer reaches back to the start — the plan reports:
actionisno_opreasonisrelease_version_unavailablemessagenames which end of the span the source falls short of
Executing that sync returns top-level status: waiting_for_source (not up_to_date,
which would claim the dataset is current when a newer release is declared).
Clients should treat waiting_for_source as "retry later": nothing was written, the existing
artifact and its version are untouched, and the same request succeeds once the source
publishes the declared release. Where the source can preserve existing coverage but reaches
only partway to the requested end, sync proceeds and clamps target_end to what is available,
reporting target_end_source as plugin_availability.
This guard is only as good as each plugin's periods() reporting. A plugin that enumerates
periods without regard to the revision it was configured with — WorldPop's returns a fixed
2015–2030 year list — reports every period as available even for a revision the upstream hub
has not published, so advancing such a template to an unpublished revision fails at fetch time
rather than returning waiting_for_source.
Configured availability policies:
- CHIRPS3 daily uses
open_climate_service.providers.availability.chirps3_daily_latest_available; this clamps sync targets to the latest complete released source month - ERA5-Land hourly uses
open_climate_service.providers.availability.lagged_latest_availablewith a YAML-declaredlag_hours - WorldPop yearly uses
open_climate_service.providers.availability.worldpop_release_latest_available; this can allow configured future projection years
Example dry-run plan:
Example execution:
curl -s -X POST "http://127.0.0.1:9000/sync/chirps3_precipitation_daily" \
-H "Content-Type: application/json" \
-d '{"end":"2024-02-10","publish":true}' | jq
Manual Test Sequence¶
These commands assume the API is running on http://127.0.0.1:9000 and that
jq is available.
1. Confirm configured extent¶
2. Create an initial CHIRPS3 managed dataset¶
curl -s -X POST "http://127.0.0.1:9000/ingestions" \
-H "Content-Type: application/json" \
-d '{
"dataset_id": "chirps3_precipitation_daily",
"start": "2024-01-01",
"end": "2024-01-31",
"publish": true
}' | jq
Expected:
statusiscompleteddataset.dataset_idischirps3_precipitation_dailydataset.extent.temporal.endis2024-01-31
3. Inspect the managed dataset and publication¶
curl -s "http://127.0.0.1:9000/datasets/chirps3_precipitation_daily" | jq
curl -s "http://127.0.0.1:9000/stac/collections/chirps3_precipitation_daily" | jq
curl -s "http://127.0.0.1:9000/zarr/chirps3_precipitation_daily" | jq
4. Dry-run a CHIRPS3 append sync¶
Expected planning response:
sync_kindistemporalactionisappendreasonisnew_periods_available_for_appendmessageexplains that existing data is present and which missing period range will be downloadedcurrent_startis2024-01-01current_endis2024-01-31target_endis2024-02-10target_end_sourceisrequestdelta_startis2024-02-01delta_endis2024-02-10
append here means Open Climate Service reuses the planner's source-available delta and
writes only missing periods to the existing Icechunk store. A rollback snapshot protects
the previously committed store until normalization and artifact registration succeed.
Progress counts the new periods in this append, excluding already committed history.
If a complete store has lost its artifact record, repeating /ingest reconstructs the
record from the store without querying or fetching historical source data. Recovery
validates and normalizes the store and honors the request's publication setting.
Interrupted directory rollback can leave a rejected replacement at <store>.failed.
The next ingest restores a missing target from <store>.retired and removes the
rejected copy once the target exists. If rollback itself fails, the job reports that
failure and preserves the recovery branch and snapshot; inspect the reported store
paths before retrying. Snapshot reset is skipped if the original repository could
not be restored.
Where these timestamps come from:
current_startandcurrent_endcome from the latest stored artifact coveragetarget_endcomes from the explicitendquery parameter, or defaults to today in the dataset-native period format when omittedtarget_end_sourcetells you whethertarget_endcame fromrequest,default_today,current_coverage, or was clamped by source availabilitydelta_startis the first period aftercurrent_enddelta_endis the resolved target period after any availability clamping
If end is omitted, the planner defaults to the current date. For example, calling
/sync/chirps3_precipitation_daily/plan on 2026-04-20 after ingesting
through 2024-01-31 first resolves the target from today's date, then applies
CHIRPS3 availability. Because CHIRPS3 daily sync is configured to use complete
released source months, the target may be clamped below today's date and
target_end_source will be default_today_clamped_by_availability.
For controlled tests, always pass an explicit end. If the explicit end
extends beyond the configured provider availability, target_end_source will be
request_clamped_by_availability.
Availability clamping example¶
If CHIRPS3 currently has complete released data through 2026-03-31 and you ask
for:
Expected:
target_endis2026-03-31target_end_sourceisrequest_clamped_by_availability- the sync does not ask the upstream downloader for unavailable April daily data
5. Execute the CHIRPS3 sync¶
curl -s -X POST "http://127.0.0.1:9000/sync/chirps3_precipitation_daily" \
-H "Content-Type: application/json" \
-d '{
"end": "2024-02-10",
"publish": true
}' | jq
Expected:
statusiscompletedsync_detail.actionisappendsync_detail.current_endwas2024-01-31sync_detail.delta_startis2024-02-01sync_detail.delta_endis2024-02-10sync_detail.target_endis2024-02-10- the returned
dataset.dataset_idis stillchirps3_precipitation_daily - the returned dataset has a newer version in
versions - the returned dataset coverage ends at
2024-02-10, even if the upstream downloader cached the full February source month
You can then extend the same managed dataset again:
curl -s -X POST "http://127.0.0.1:9000/sync/chirps3_precipitation_daily" \
-H "Content-Type: application/json" \
-d '{
"end": "2024-02-20",
"publish": true
}' | jq
Expected:
sync_detail.current_endwas2024-02-10sync_detail.delta_startis2024-02-11sync_detail.delta_endis2024-02-20- returned dataset coverage ends at
2024-02-20 - execution may be fast when the provider cache already contains the needed source files
6. Confirm no-op behavior¶
Run the same plan again with the current end:
Expected:
actionisno_opreasonisno_new_periodmessageexplains that the requested target is already covered locally
7. Test release-style sync with WorldPop¶
Create an initial WorldPop managed dataset:
curl -s -X POST "http://127.0.0.1:9000/ingestions" \
-H "Content-Type: application/json" \
-d '{
"dataset_id": "worldpop_population_global2_100m",
"start": "2020",
"end": "2020",
"publish": true
}' | jq
Plan a later release:
Expected:
sync_kindisreleaseactionisrematerializereasonisnew_release_availabletarget_endis2021
Execute the release sync:
curl -s -X POST "http://127.0.0.1:9000/sync/worldpop_population_global2_100m" \
-H "Content-Type: application/json" \
-d '{
"end": "2021",
"publish": true
}' | jq
Expected:
statusiscompletedsync_detail.actionisrematerializedataset.dataset_idisworldpop_population_global2_100m
8. Observe release identity¶
Plan again with the end you just materialized:
curl -s "http://127.0.0.1:9000/sync/worldpop_population_global2_100m/plan?end=2021" | jq \
'{action: .action, reason: .reason, current_version, target_version}'
Expected:
current_versionandtarget_versionare both{"value": "R2025A", "authority": "worldpop"}actionisno_opandreasonisno_new_release— matching releases fall through to the period comparison
To see a release change drive a sync, edit the template's sync.version.value (and the
plugin's matching ingestion.params.revision) to a published revision and plan again.
Expected:
actionisrematerializereasonisrelease_version_changedtarget_endis unchanged from the current coverage end — a version change rewrites the existing span rather than truncating it to today
If the declared revision is not yet published by the source, the same plan returns no_op
with reason: release_version_unavailable; executing the sync returns
status: waiting_for_source, provided the plugin's periods() reports availability for that
revision — see Release identity above.
Summary¶
The current branch is no longer an artifact-first API.
The public contract is now:
- ingest with
/ingestions - discover the configured extent with
/extent - discover managed datasets with
/datasets - discover published Zarr-backed datasets with
/stac/catalog.json - access raw native data with
/zarr/{dataset_id}
Artifacts remain internal because Open Climate Service still needs storage and provenance records behind ingestion and publication, but those internals are no longer exposed as first-class public resources.