Schema Contracts for Vector/Tile Data

The versioned producer–consumer agreements that keep vector and tile payloads interoperable as they cross domain boundaries in the federated mesh.

Enterprise geospatial platforms built on domain-driven ownership cannot rely on a shared geodatabase to keep producers and consumers in step — each domain ships its own vector features and tile pyramids on its own cadence, and the only thing standing between them is the contract. This page sits within the Federated Ownership & Routing Architecture, and it treats a schema contract not as documentation but as an enforceable, signed artifact that the routing plane validates twice: once in CI/CD when a producer publishes, and again at runtime ingestion before a payload reaches a consumer. Without that double gate, schema drift propagates silently — a producer adds a nullable attribute, flips a coordinate order, or bumps a tile matrix set, and every downstream analytic quietly degrades. The contract surface defined here is the same one the API Gateway Mapping for GIS Services enforces at the edge, so a geometry that passes producer-side CI also passes at ingress.

Figure — Contract-first interoperability: versioned schemas are enforced in CI/CD and again at runtime before data reaches consumers.

Contract-first interoperability gate A producer domain publishes to a schema registry of versioned JSON Schema and Protobuf definitions. The contract is enforced at two gates: a CI/CD contract check that blocks a merge on failure, and a runtime ingestion check that admits valid payloads to the consumer domain. Either gate failing routes the payload to a 422 contract violation rejection instead of the consumer. Producer domain Schema registry versioned JSON Schema & Protobuf defs CI/CD contract check Runtime ingestion check Consumer domain Reject 422 contract violation pass valid fail invalid

Architectural Boundaries & Design Rationale

A schema contract exists to make a domain boundary into a trust boundary. Vector formats and tile pyramids carry structural invariants that a generic JSON validator never sees: a coordinate reference system declaration, a winding order, a topology rule, a tile-grid alignment, a zoom-level range. The contract pins all of these as explicit assertions at the boundary, so that interoperability is guaranteed by construction rather than discovered in production. For vector formats the contract enforces CRS declarations, property type strictness, and topology validity; for tile-based formats it extends to grid alignment, zoom-level constraints, and geometry clipping tolerances. The concrete validation rules and transformation fallbacks for legacy and modern vector encodings are documented in Enforcing schema contracts for GeoJSON and Shapefiles, which this page treats as its reference implementation for cross-format compatibility.

Enforcing the contract at the boundary prevents three failure modes that otherwise corrode a federated estate. First, silent projection drift: a producer that quietly switches from EPSG:4326 to EPSG:3857 without bumping its contract version misaligns every consumer join until someone notices the offset months later. Second, topology contamination: self-intersecting polygons or unclosed rings that pass a naive schema check but break spatial indexes and tile clipping downstream. Third, implicit coupling: a consumer that comes to depend on an undeclared attribute, so that the producer can never safely evolve its product. By validating against a versioned, registry-published definition, the boundary rejects all three with a deterministic 422 rather than admitting them into a domain where they would corrupt state. When schema versions legitimately diverge across domains, the routing plane does not fail outright — it triggers reconciliation through the Domain Sync Protocols for Spatial Data, which negotiate version compatibility, propagate schema patches, and maintain eventual consistency across distributed tile caches. Contract-aware dispatch at the edge, in turn, is the job of Cross-Domain Routing Strategies, which read a payload’s contract fingerprint to select the correct transformation layer so a consumer requesting v2 vector data receives properly projected, clipped output without forcing the producer to maintain a backward-compatible endpoint.

Specification & Contract Reference

A contract is defined at the domain boundary before ingestion or distribution, published as a versioned JSON Schema or Protobuf definition in a central registry, and referenced by an immutable identifier that encodes both CRS and resolution — following the site convention v1.2.0-crs:EPSG:4326-res:10m. The fields below are the minimum surface a vector/tile contract must declare before the routing plane will admit a payload.

Field / Parameter Scope Required Constraint / Default
contract_id Manifest Yes Stable product identifier; missing value rejected 400
schema_version Manifest Yes SemVer with CRS/res suffix, e.g. v1.2.0-crs:EPSG:4326-res:10m
crs Geometry Yes One of EPSG:4326, EPSG:3857, EPSG:32633; unsupported → 422 crs_unsupported
Coordinate order Geometry Yes Longitude, latitude per RFC 7946; reversed order rejected
crs member (GeoJSON) Geometry No Forbidden — RFC 7946 mandates WGS 84; presence → 422 crs_member_forbidden
Coordinate precision Geometry Yes Decimals capped at 7 (≈1 cm); excess vertices flagged as amplification risk
bbox Geometry Conditional minx,miny,maxx,maxy in declared CRS; clamped to [-180,-90,180,90] for EPSG:4326
Topology rule Geometry Yes OGC Simple Features validity; self-intersecting/unclosed → reject
Tile matrix set Tile Tile only Aligned to OGC API - Tiles matrix definitions
Zoom range Tile Tile only Declared levels (e.g. 8,10,12,14); out-of-range request → 404
Clipping tolerance Tile Tile only Snap-to-grid tolerance in CRS units (e.g. 0.001)
Accept-Spatial-Contract Header Yes Pins requested contract version; absent → reject unversioned request
Z/M dimensions Geometry No Stripped unless contract explicitly permits

Contracts must align with established open standards rather than per-domain dialects. For GeoJSON payloads, strict adherence to RFC 7946 coordinate ordering and CRS handling is mandatory — RFC 7946 removes support for the crs member and mandates WGS 84 (EPSG:4326), so any payload that smuggles a crs member is non-conformant by definition. Tile contracts reference OGC API - Tiles to keep matrix set definitions and MIME-type routing consistent across domains. These are the same versioned definitions the gateway pins at the edge, which is why a contract bump is a cross-cutting event: it propagates from the registry to CI, to the ingestion check, and to the route table together.

Production Implementation

Validation runs in two places. In CI/CD it executes synchronously when a producer publishes, blocking a merge on any contract violation. At runtime it executes during ingestion, rejecting payloads that violate declared property types, omit mandatory spatial fields, or exceed bounding-box limits. Heavy spatial operations — topology validation, spatial joins, tile generation — exceed synchronous request timeouts, so contract-validated payloads are routed into dedicated async queues that isolate compute-intensive work from the ingestion control plane. Each job carries a contract fingerprint that the worker uses to select the correct pipeline; the queue topology and worker scaling behind these jobs belong to Async Execution for Heavy Spatial Queries.

Idempotency is mandatory and zero-trust is assumed. Every spatial job is keyed on a deterministic hash combining the payload SHA-256, the contract version, and the target CRS, so retry storms cannot duplicate tile generation or corrupt a spatial index. The worker below enforces exactly-once semantics with a Redis SET NX guard before it validates and processes.

python
# Idempotent async contract worker. Zero-trust: the schema is fetched from the signed
# registry by the caller and passed in — the worker never trusts an inline schema.
import hashlib
import json

from celery import Celery
from jsonschema import validate, ValidationError
from redis import Redis

app = Celery("spatial-contract-worker")
redis_client = Redis(host="redis-mesh", decode_responses=True)


def compute_job_id(payload: dict, contract_version: str, target_crs: str) -> str:
    # Deterministic key: payload digest + contract version + CRS guarantees retries converge.
    payload_hash = hashlib.sha256(
        json.dumps(payload, sort_keys=True).encode()
    ).hexdigest()
    return f"{payload_hash}:{contract_version}:{target_crs}"


@app.task(bind=True, max_retries=3, default_retry_delay=30)
def process_vector_contract(self, payload: dict, contract_version: str,
                            target_crs: str, schema: dict):
    job_id = compute_job_id(payload, contract_version, target_crs)

    # Idempotency guard: SET NX returns True only on the first invocation.
    if redis_client.set(job_id, "processing", nx=True, ex=3600):
        try:
            validate(instance=payload, schema=schema)
            # Topology validation, tile generation, or spatial join run here.
            redis_client.set(job_id, "completed", ex=86400)
        except ValidationError as e:
            # Record the failure for diagnostics, then retry with backoff.
            redis_client.set(job_id, f"failed:{e.message}", ex=86400)
            raise self.retry(exc=e)
    # If NX returned False a prior invocation is already running or done — skip safely.

The validation pipeline itself is declared as versioned configuration, not hand-wired in code, so that a contract change is reviewed and promoted through GitOps alongside the schema it enforces. The pipeline pulls signed definitions from the registry and applies staged rules — CRS enforcement, bounding-box clamping, tile-grid snapping — each with an explicit action.

yaml
# Contract validation pipeline. signing_verification rejects any registry artifact
# whose cosign signature does not verify, closing the supply-chain gap.
contract_registry:
  endpoint: https://schema-registry.mesh.internal/v1
  cache_ttl: 300s
  signing_verification: true

validation_pipeline:
  stages:
    - name: crs_enforcement
      rules:
        - field: "$.crs"
          allowed: ["EPSG:4326", "EPSG:3857", "EPSG:32633"]
          action: reject            # unsupported CRS → 422 crs_unsupported
    - name: topology_bounds
      rules:
        - field: "$.bbox"
          max_extent: [-180.0, -90.0, 180.0, 90.0]
          action: clamp             # out-of-bounds coordinates are clamped, not silently kept
    - name: tile_grid_alignment
      rules:
        - zoom_levels: [8, 10, 12, 14]
          clipping_tolerance: 0.001
          action: snap_to_grid      # enforce OGC API - Tiles matrix alignment

Payload sanitization runs before schema validation, never after: coordinates are clamped to the declared bounding box, Z/M dimensions are stripped unless the contract permits them, and external geometry references are blocked outright. Contract manifests are cryptographically signed so a consumer can verify provenance and version integrity before it consumes — the signing_verification flag above is what enforces that the registry never serves an unsigned schema.

Diagnostic Runbook

When a contract violation interrupts service, the fault is almost always drift between a producer schema and the pinned ingestion schema, a missing version header, or a registry sync lag — not the geometry itself. Work the steps in order; each isolates one boundary the contract enforces.

  1. Recover the violation record. Pull the structured telemetry for the failed request and read contract_id, schema_version, error_code, spatial_hash, and failure_stage. A failure_stage of runtime_ingestion versus ci_cd tells you immediately whether a producer shipped bad data or a payload mutated in flight.
  2. Confirm the version header. Verify the request carried Accept-Spatial-Contract. A missing header collapses the request to implicit negotiation and surfaces as a spurious rejection — the gateway is configured to reject unversioned requests by design.
  3. Diff the schema versions. Compare the schema_version the producer published against the version pinned at the ingestion check. A mismatch is contract drift; resolve it through the reconciliation routines in the domain sync protocols rather than by hand-editing the route.
  4. Inspect the CRS and coordinate order. Search the validator log for crs_unsupported, crs_member_forbidden, or reversed-axis flags. A GeoJSON payload carrying a crs member, or coordinates in latitude-longitude order, fails RFC 7946 conformance here before any topology check runs.
  5. Check topology and precision. Look for self-intersection, unclosed-ring, or vertex-count-amplification flags. These pass a naive type check but break spatial indexes and tile clipping downstream, so the contract rejects them at the boundary.
  6. Verify registry sync and signature. Confirm the ingestion node fetched the current signed schema within its cache_ttl; a stale or unsigned artifact (signature failure) blocks every payload for that contract. Force a registry refresh if drift exceeds the budget below.
  7. Trace the fallback path. If service is degraded rather than failing, confirm the fallback chain activated — substituting validated default geometries, routing to a cached tile version, or enqueuing on-demand re-validation — and that distributed tracing propagated the spatial_hash through every hop so the drift source is attributable.

SLA Targets & Performance Baselines

Contract enforcement must add thin, predictable overhead; the heavy spatial work belongs to the async backends. Pre-compiling validation rules into WebAssembly modules at the edge keeps schema evaluation in sub-millisecond ranges, and immutable registry snapshots let recovery playbooks replay ingestion logs against the last known-good contract during a regional outage. The targets below are the budget the contract layer must hold.

Metric Target Alert Threshold Remediation Action
Schema validation latency < 1ms p99 > 5ms p99 for 5m Pre-compile rules to WASM; pin schema in memory
Contract violation rate < 0.5% > 1% for 5m Trip circuit breaker; quarantine producer pipeline
Registry sync drift < 2s > 30s Force registry refresh; check signing service availability
Idempotency cache hit ratio > 0.98 on retries < 0.90 Verify Redis TTL and spatial_hash key propagation
Signature verification success > 99.99% < 99.9% Rotate cosign keys; block unsigned artifacts
Async job submission < 50ms p99 > 120ms p99 Inspect queue depth and worker pool saturation
Fallback activation latency < 100ms > 500ms Pre-warm cached tile versions; check default-geometry store

Async execution boundaries are protected by circuit breakers that halt processing when the validation error rate exceeds the 0.5% budget, so a flood of malformed geometries cannot exhaust the worker pool. Holding these baselines depends on event-driven cache invalidation — a contract version bump or a registry update, not a fixed expiry, is what evicts a stale schema.

Governance & Compliance Notes

Contract enforcement is inseparable from security posture, so every validation decision is an auditable artifact. Ingestion endpoints operate under zero-trust: attribute-based access control ties spatial data permissions directly to contract domains, and a consuming domain that requests vector data outside its authorized contract scope receives field-level redaction and geometry generalization rather than the raw product. Audit trails capture every validation rejection, policy override, and schema migration in append-only, tenant-partitioned streams, providing immutable evidence for compliance reviews and SLA-breach investigations. These records map back to the governed catalog entries described in Metadata Cataloging for Raster/Vector, so a consumer-visible contract corresponds to a documented, owned data product.

The diagnostic query below is the standard governance read against the validation log — it surfaces which contracts are drifting and how badly, which is the input both to an SLA review and to a producer-quarantine decision.

sql
-- Governance read: rank active runtime contract violations in the last hour.
SELECT
  contract_id,
  schema_version,
  COUNT(*)                                   AS violation_count,
  AVG(validation_latency_ms)                 AS avg_latency,
  STRING_AGG(DISTINCT error_code, ', ')      AS active_errors
FROM spatial_validation_logs
WHERE failure_stage = 'runtime_ingestion'
  AND timestamp > NOW() - INTERVAL '1 hour'
GROUP BY contract_id, schema_version
ORDER BY violation_count DESC;

Where jurisdictional constraints apply — data-residency rules binding a tenant’s vector features to a region — the contract enforces them at the boundary, refusing to admit a payload whose declared bbox or domain context falls outside the permitted extent. Schema migrations are governed the same way routes are: validators and signing keys are committed to source control, reviewed, and promoted only after CI verifies schema compliance and replays idempotency behavior. Anchoring ingestion to versioned, signed contracts — isolating heavy spatial workloads behind idempotent queues and routing traffic through contract-aware gateways — is what lets a federated geospatial estate achieve predictable performance, secure cross-domain exchange, and resilient interoperability without a central choke point.