Algorithm Manifests (v1)

Complete guide to defining v1 algorithm manifests

Algorithm Manifests (v1)

An algorithm manifest is a JSON document that describes your algorithm's requirements, capabilities, and behavior. The platform validates this manifest against the manifest schema when you register an algorithm.

This page documents manifest schema version 1.0.0. Manifests declaring manifest_version: 0.1.0 remain valid and are documented in Algorithm Manifests; see Migrating from 0.1.0 below for the mapping between the two.

Complete Example

Here's a complete manifest for a ship detection algorithm running as a container:

{
  "manifest_version": "1.0.0",
  "metadata": {
    "description": "Wide-area ship detection on high-resolution Planet SkySat imagery, producing rotated bounding box detections and classifying ships into 8 commercial, passenger, and military classes.",
    "tags": ["skysat_ship"]
  },
  "runtime": {
    "type": "container",
    "image": "orbitalinsight/skysat_ship_d2:64361851",
    "command": ["python", "/orbital/base/algorithms/skysat_ship_d2/src/py/skysat_ship_d2/simple_inference.py"],
    "resource_request": {
      "gpu": 1,
      "memory_gb": 16,
      "cpu_millicore": 1000
    }
  },
  "io": {
    "interface": "paths",
    "inputs": [
      {
        "data_type_name": "planet_SkySatScene",
        "parameters": {
          "image_processing_spec": "PL-SkySatScene",
          "bands": [91]
        }
      }
    ],
    "outputs": [
      {
        "data_type_name": "multiclass_object_detection"
      }
    ]
  },
  "capability": {
    "classes": ["aircraft_carrier", "large_cargo", "military_warship", "cruise_ship",
                "submarine", "tugboat", "small_vessel", "other_ship"],
    "metrics": {
      "f1_score": 0.6746,
      "precision": 0.7223,
      "recall": 0.6329,
      "per_class": [
        {"class": "aircraft_carrier", "precision": 0.7931, "recall": 0.76, "f1_score": 0.7762},
        {"class": "large_cargo", "precision": 0.7950, "recall": 0.75, "f1_score": 0.7719},
        {"class": "military_warship", "precision": 0.7602, "recall": 0.68, "f1_score": 0.7179},
        {"class": "cruise_ship", "precision": 0.8266, "recall": 0.82, "f1_score": 0.8333},
        {"class": "submarine", "precision": 0.9115, "recall": 0.80, "f1_score": 0.8521},
        {"class": "tugboat", "precision": 0.7360, "recall": 0.62, "f1_score": 0.6731},
        {"class": "small_vessel", "precision": 0.6618, "recall": 0.67, "f1_score": 0.6659},
        {"class": "other_ship", "precision": 0.5106, "recall": 0.49, "f1_score": 0.5001}
      ]
    },
    "coverage": "Trained on 2,843 Planet SkySat scenes over open water and coastal areas across 6 continents (April 2020 - April 2021), with roughly 10% adverse-weather imagery.",
    "location": "Open and coastal waters worldwide; performs best in calm water where ships are not packed closely together.",
    "known_issues": "Reduced accuracy in dense anchorages and dark areas, and under snow, haze, or cloud. High appearance variance within a class (e.g. military warships) can degrade classification."
  },
  "parameters": [
    {
      "name": "sts_enabled",
      "type": "boolean",
      "description": "Enable ship-to-ship transfer detection on detected vessels.",
      "default": false
    },
    {
      "name": "ignore_aoi",
      "type": "boolean",
      "description": "Turn off AOI filtering on detections.",
      "default": false
    },
    {
      "name": "get_small_vessel",
      "type": "boolean",
      "description": "Turn on small vessel detections.",
      "default": false
    }
  ],
  "dimensions": [
    {"name": "AOI"},
    {"name": "SCENE_ID"}
  ]
}

Different algorithms require different runtime engines. This is specified using the runtime block. For example, an algorithm that is run using a Triton inference service would have the following runtime block:

{
  "runtime": {
    "type": "triton",
    "image": "orbitalinsight/skysat_ship_prepost:a1b2c3d4",
    "command": ["python", "-u", "/app/prepost.py"],
    "model_name": "f0cd3037-8a2e-4b6d-9c15-7e3f5a1b8d42",
    "model_architecture": "yolox",
    "tensor_input": {"channels": 3, "height": 640, "width": 640},
    "resource_request": {
      "gpu": 0,
      "memory_gb": 8,
      "cpu_millicore": 1000
    }
  }
}

And an algorithm that runs as a Flink stream-processing job ships a jar rather than a container:

{
  "runtime": {
    "type": "flink",
    "jar": "s3://oi-artifacts/track-enrichment-1.4.2.jar",
    "entry_class": "com.orbitalinsight.tracks.EnrichmentJob",
    "parallelism": 4
  }
}

Manifest Structure

The following four fields are required: manifest_version, metadata, runtime, and io. The rest (capability, lineage, parameters, and dimensions) are optional.

v1 manifests are validated strictly: fields that are not part of the schema are rejected.

manifest_version (required)

Specifies which version of the manifest schema to use, in semantic-version form.

{
  "manifest_version": "1.0.0"
}

Current version: "1.0.0"

The platform validates your manifest against the schema for this version.

metadata (required)

Provides high-level information about your algorithm.

{
  "metadata": {
    "description": "Detect and count railcars in optical imagery.",
    "tags": ["railcar", "rail", "object_detection"]
  }
}

Fields:

  • description (string, required) - Clear description of the algorithm's purpose
  • tags (array of strings, optional) - Keywords for categorization and search

What the algorithm can do, how well, and where it works belongs in capability, not here.

Note: there is no name field. The algorithm's human-readable name belongs to the Algorithm entity and is supplied at registration time — renaming an algorithm does not create a new Algorithm Version. There is also no version field: Algorithm Versions are identified by the SHA-256 hash of the manifest — see Manifest Versioning.

runtime (required)

Specifies the execution environment for the algorithm: what runs, where the model executes, and what resources it needs.

{
  "runtime": {
    "type": "container",
    "image": "myorg/my-algorithm:v1.0.0",
    "command": ["python", "-u", "/app/main.py"],
    "resource_request": {
      "gpu": 0,
      "memory_gb": 8,
      "cpu_millicore": 1000,
      "max_input_gb": 1
    }
  }
}

Fields:

  • type (string, required) - one of:
    • container - the algorithm runs entirely in its own container (the standard runtime)
    • triton - the algorithm container performs pre/post processing and calls the shared Triton inference service for model inference
    • flink - the algorithm runs as a job on the Flink stream-processing engine, shipped as a jar - there is no container, so no image or command
  • image (string, required for container and triton) - Full container image reference (registry/name:tag)
  • command (array of strings, required for container and triton) - Container command in exec form (e.g. ["python", "main.py"], not "python main.py")
  • resource_request (object, optional) - Resource requirements:
    • gpu (number) - Number of GPUs (0 for CPU-only)
    • memory_gb (number) - Memory in gigabytes
    • cpu_millicore (number) - CPU in millicores (1000 = 1 core)
    • max_input_gb (number) - Maximum size in GB for the algorithm input

Additional fields for type: triton:

  • model_name (string, required for triton) - The model to invoke on the inference server. The server endpoint is resolved by the platform at run time and injected into algo_input.json; it is never part of the manifest.
  • model_architecture (string, optional) - The architecture of the served model (e.g. "yolox")
  • tensor_input (object, optional) - The served model's input tensor shape: channels, height, width

For type: triton, the GPU lives with the shared Triton service, so the algorithm container is typically CPU-only ("gpu": 0).

Additional fields for type: flink:

  • jar (string, required for flink) - Reference to the job jar artifact
  • entry_class (string, optional) - Fully qualified entry class of the job; required if the jar's manifest does not declare one
  • parallelism (number, optional) - Flink job parallelism; resource_request does not apply — the job's resources are governed by the Flink engine

Runtime types name what the algorithm author ships — a container, a Triton-served model with its pre/post container, a Flink jar — never the platform service that schedules the run. New types arrive in a schema revision; validation rejects values the schema doesn't know.

Best practices:

  • Use specific image tags, not latest; include the image digest for reproducibility.
  • Request resources conservatively but adequately; monitor actual usage during test runs.
  • Use the -u flag with Python for unbuffered output.

io (required)

Specifies how data gets into and out of the algorithm: the I/O interface it implements, the Data Types it consumes and produces, and — for imagery algorithms that need scenes grouped in a particular way — an optional scene_selection block.

{
  "io": {
    "interface": "urls",
    "inputs": [
      {
        "data_type_name": "pings",
        "min_count": 1,
        "max_count": 1
      }
    ],
    "outputs": [
      {
        "data_type_name": "device_visits"
      }
    ]
  }
}

io.interface (required)

Declares which I/O contract the algorithm's code implements. This is an author declaration, not a hint: each value requires different code inside the algorithm container, and the platform provides a different environment for each.

interfaceDocumentsThe platformThe algorithm
pathsv1downloads all input data to local disk before the runreads local filesystem paths from algo_input.json
urlsv2downloads nothing; composes each input's data-source query as a ready-to-fetch URL and injects itfetches the URLs itself — any URL-capable reader (fsspec, GDAL, pandas) works
apiv2downloads nothing; injects data-source endpoints and scoped credentialscomposes its own queries, e.g. through the data source's client library

Notes on each value:

  • paths is the backwards-compatible contract: existing algorithms adopt this manifest schema without container changes by declaring it.
  • urls entries are ready to fetch exactly as given — the query is already written, and any required authorization is already embedded in the URL. An urls algorithm can only be paired with data sources whose queries are expressible as URL parameters; registration validates this.
  • api is for algorithms that drive a data source themselves — parameterized queries (time windows, geohashes, filters) through the source's API or client library.

urls and api are the same thing underneath — data-source API access — differing only in who writes the query. Both run behind the platform trust boundary: pod egress is allowlisted to the injected endpoints, and credentials are scoped and short-lived.

Every value exchanges the same pair of documents: input via an algo_input.json file (path in the ALGORITHM_INPUT_PATH environment variable), output written to the supplied output_path. paths uses the v1 document schemas; urls and api use v2. See Algorithm Input/Output.

Future interface values name drivable contracts — a documented protocol the platform (or a bridging sidecar) can be implemented against, such as a vendor ICD (e.g. airbus_icd) — never bare transports like http.

io.inputs (required)

Declares the Data Types your algorithm consumes. This is an array — one entry per input Data Type.

Fields per input:

  • data_type_name (string, required) - Name of the Data Type (must exist in the platform; retrieve with DataTypesGetRequest)
  • min_count (integer, optional, default 1) - Minimum number of data sources required
  • max_count (integer, optional, default 1) - Maximum number of data sources allowed
  • parameters (object, optional) - Data-Type query parameters conforming to the Data Type's query schema (e.g. image_processing_spec, bands, lookback)

Notes:

  • Most algorithms have a single input Data Type.
  • Use multiple inputs if your algorithm combines different data types.
  • Set min_count: 0 for optional inputs.
  • Inputs declare data types, not data sources. Which data source supplies a type is chosen in the Algorithm/Analysis Configuration, not the manifest.

io.outputs (required)

Declares the Data Types your algorithm produces. This is an array — one entry per output Data Type (each entry's own observation_value_columns lists that type's summary metrics).

Fields per output:

  • data_type_name (string, required) - The output Data Type

Outputs are objects even though they carry a single field today — consistent with inputs, and open to per-output fields later. The classes an algorithm detects are declared in capability, not per output.

A summary rollup (a detection count, a unique-device count) is not declared on an output — it is its own output with a dedicated data type (e.g. detection_count), written by the algorithm alongside its detail output. The columns an output contains are described by its Data Type. See Algorithm Output Schema for the full output structure.

io.scene_selection (optional)

A declarative block consumed by the platform's scene-selection step. Use it for algorithms that need scenes grouped or paired in a particular way (e.g. stereo pairs, before/after change detection). It refines io.inputs: inputs declare which Data Types the algorithm consumes, scene_selection declares which scenes qualify and how they combine into a run.

{
  "io": {
    "scene_selection": {
      "grouping_strategy": "pair",
      "temporal_strategy": "window",
      "spatial_strategy": "independent",
      "min_count": 2,
      "lookback_windows": 0,
      "spacing": {
        "min_separation": "P10D",
        "max_separation": "P40D"
      },
      "data_source_filters": ["cloud_cover <= 20"]
    }
  }
}

Fields:

  • grouping_strategy (required) - how scenes are grouped: single, independent, pair, cross
  • temporal_strategy (required) - how time is sliced: all, window, event
  • spatial_strategy (required) - how AOIs combine with grouping: independent, cross_aoi
  • min_count (integer, optional, default 1) - minimum scenes per group
  • spacing (object, optional) - temporal spacing between scenes within a group (min_separation / max_separation, ISO 8601 durations); only valid for pair or cross
  • data_source_filters (array, optional) - CQL2 filter expressions that replace the data source's default constraints for this algorithm (user cql2_filters AND on top)
  • grouping_filters (array, optional) - CQL2 filter expressions for grouping, using reference.* / target.* prefixes
  • before_search / after_search (ISO 8601 durations) - required when temporal_strategy is event
  • lookback_windows (integer) - required when temporal_strategy is window; 0 means current window only (e.g. stereo pairs)

capability (optional)

What the algorithm can do, how well, and where it works — the block a user (or an agent) reads to decide whether the algorithm fits their use case.

{
  "capability": {
    "classes": ["fighter", "bomber"],
    "metrics": {
      "f1_score": 0.87,
      "precision": 0.91,
      "recall": 0.83,
      "per_class": [
        {"class": "fighter", "precision": 0.92, "recall": 0.85, "f1_score": 0.88},
        {"class": "bomber", "precision": 0.89, "recall": 0.81, "f1_score": 0.85}
      ]
    },
    "coverage": "Trained on ~100,000 labeled aircraft across 20,000 scenes worldwide.",
    "location": "Works best at airfields and military bases.",
    "known_issues": "Reduced accuracy on heavily shadowed or hazy imagery."
  }
}

Fields:

  • classes (array of strings, optional) - The classes the algorithm detects or classifies. Algorithm-level: one vocabulary regardless of how many outputs the algorithm produces. For Model Studio models it is filled from the model's categories at registration.
  • metrics (object, optional) - Reported accuracy: f1_score, precision, and recall across all classes, plus per_class (array of class, precision, recall, f1_score). per_class entries reference names in classes.
  • coverage (string, optional) - What data the algorithm was trained and evaluated on
  • location (string, optional) - Where/when the algorithm works best
  • known_issues (string, optional) - Known limitations

lineage (optional)

Records where the algorithm's model came from. For models trained in Model Studio, these fields are filled from the export automatically at registration; agents and users read them to judge whether a model fits their use case.

{
  "lineage": {
    "source": "model-studio",
    "project_id": "7c2a91e4-31f8-4b0a-9d55-8f2e6a7c4d13",
    "experiment_id": "2f8b03d7-6a91-4c25-b7e8-1d4f9c0a5e66",
    "run_id": "91e5c6a2-4d78-4f31-a2b9-6c8e0d3f7a24",
    "action_id": "f0cd3037-8a2e-4b6d-9c15-7e3f5a1b8d42",
    "dataset": {
      "dataset_id": "5d417f9b-2c86-4e03-9a71-3b8d6f2c9e48",
      "dataset_commit_id": "8a30c1e6-7f24-4d95-b1c8-2e6a9d4f0b37",
      "num_samples": 18450
    }
  }
}

Fields:

  • source (string, optional) - The system that produced the model (e.g. "model-studio")
  • project_id / experiment_id / run_id (strings, optional) - The Model Studio training lineage
  • action_id (string, optional) - The Model Studio export action id — the stable key joining this Algorithm Version to the exported model
  • dataset (object, optional) - The dataset the model was trained on: dataset_id, dataset_commit_id, num_samples

parameters (optional)

Defines configurable parameters users can set when creating an Algorithm Config.

{
  "parameters": [
    {
      "name": "confidence_threshold",
      "type": "double",
      "description": "Minimum confidence score for detections",
      "units": "probability",
      "min": 0.0,
      "max": 1.0,
      "default": 0.7
    },
    {
      "name": "enable_filtering",
      "type": "boolean",
      "description": "Apply post-processing filters",
      "default": true
    },
    {
      "name": "model_variant",
      "type": "string",
      "description": "Which model variant to use",
      "allowed_values": ["fast", "standard", "accurate"],
      "default": "standard"
    }
  ]
}

Fields per parameter:

  • name (string, required) - Parameter identifier
  • type (string, required) - One of integer, double, number, string, boolean, array
  • description (string, required) - Clear explanation of what the parameter does
  • units (string, optional) - Units for the parameter (e.g. "seconds", "meters")
  • min (number, optional) - Minimum allowed value (numeric types)
  • max (number, optional) - Maximum allowed value (numeric types)
  • allowed_values (array, optional) - Restricted set of allowed values
  • default (optional) - Default value used when a config doesn't override the parameter

The manifest declares parameters and their defaults; the chosen values for a deployment live in an Algorithm Config, never in the manifest. Changing a parameter value is a config change, not a new Algorithm Version.

Best practices:

  • Provide sensible defaults and use min/max to prevent invalid values.
  • Include units for clarity and write descriptions that explain each parameter's impact.

dimensions (optional)

Lists the Dimensions the algorithm can be parallelized by, and how each may be subdivided.

{
  "dimensions": [
    {
      "name": "AOI",
      "subdividable": true,
      "units": "geohash",
      "size": 2
    },
    {
      "name": "TIME_RANGE_GROUP"
    }
  ]
}

Fields per entry:

  • name (string, required) - the Dimension name (e.g. AOI, TIME_RANGE_GROUP)
  • subdividable (boolean, optional) - whether the Dimension can be subdivided
  • units (string, optional) - unit of the subdividable component (e.g. geohash)
  • size (number, optional) - quantity of units of the subdividable component

Note: the manifest's dimensions declares which Dimensions apply to the algorithm; the dimensions objects in algo_input.json and algorithm_output.json carry the values of those Dimensions for a specific run. Same concept, declaration vs. instance.

Validation

When you register an algorithm, the platform validates:

  1. Schema compliance - Manifest matches the schema for manifest_version; unknown fields are rejected
  2. Data Type existence - All referenced Data Types exist
  3. Data Source compatibility - Data sources support the specified Data Types (validated against DEM)
  4. Parameter validity - Parameters have valid types and constraints
  5. Resource constraints - Resources are within platform limits

Common validation errors:

Invalid manifest: 'io.outputs' must be an array
  → Fix: outputs is a list of {data_type_name, ...} objects, not an object

Data Type 'custom_type' not found
  → Fix: Create the Data Type first or use an existing one

Parameter 'threshold' missing required field 'description'
  → Fix: name, type, and description are required on every parameter

'runtime.model_name' is required when runtime.type is 'triton'
  → Fix: name the served model; the Triton endpoint itself is injected at run time

Manifest Versioning

Algorithm versions are identified by the SHA-256 hash of the manifest — there is no version number in the manifest itself.

  • Registering a manifest computes its hash. If that hash already exists for the algorithm, the existing Algorithm Version is returned unchanged (no duplicate is created).
  • Any change to the manifest produces a new hash and therefore a new Algorithm Version.
  • Algorithm Versions are immutable once registered, and are never deleted — only deactivated or deprecated.
  • The user-facing "version" label is the creation timestamp (ISO 8601) of the Algorithm Version; the latest version is the one with the most recent timestamp.

So to publish an update — a bug fix, a new model, changed parameters, or changed inputs/outputs — you simply register the updated manifest, which becomes a new Algorithm Version. For changed inputs/outputs in particular, consider whether a new Algorithm is more appropriate than a new version of the existing one.

manifest_version selects the schema, not the algorithm version: 0.1.0 manifests remain valid indefinitely and are validated against the legacy schema. Moving an algorithm's manifest to 1.0.0 is a manifest change like any other — it mints a new Algorithm Version.

Migrating from 0.1.0

0.1.0 manifests remain valid during the transition, and all algorithm manifests are being migrated to 1.0.0. Migrating a manifest does not require changing the algorithm itself — declare io.interface: paths and the container runs unchanged. The mapping:

0.1.01.0.0
container_parameters.image / .command / .resource_requestruntime.image / .command / .resource_request
runtime.type (container, triton, or flink; new, required)
interface.interface_type: FILESYSTEM_TASK_WORKERio.interface: paths
inputs / outputs (top level)io.inputs / io.outputs
parallelization[].dimensiondimensions[].name
parallelization[].subdividibledimensions[].subdividable (the spelling code actually reads)
performance_metrics.overall_f1_score / .overall_precision / .overall_recallcapability.metrics.f1_score / .precision / .recall
metrics_per_subclass[] (subclass)capability.metrics.per_class[] (class)
evaluation_detailslineage.dataset
top-level name, display_name, author, description, developer, release_statusnot in the manifest — supplied to the registration API; they describe the Algorithm entity, and changing them does not create a new Algorithm Version
metadata.name, metadata.version, metadata.release_date, metadata.indicatorremoved
restrictions, batching, tilingremoved (never used)
interface.adapter, NO_OP_WORKER, SYNCHRONOUS_TASK_HTTP_WORKERremoved; vendor contracts return as named io.interface values (e.g. airbus_icd) if needed
inputs[].data_source_ids / .data_source_id / .data_source_nameremoved — data source choice lives in the Algorithm/Analysis Configuration
outputs[].classes (and the undocumented top-level classes drift)capability.classes
metadata.training_location_coverage, metadata.best_input_location, metadata.known_issuescapability.coverage, capability.location, capability.known_issues
outputs[].observation_value_columnsremoved — aggregates are their own outputs with dedicated data types; an output's columns are described by its Data Type
outputs[].output_geometryremoved — never consumed; output records' geometry (WKB/WKT) is self-describing
inputs[].parameters.sampling, outputs[].sample_result, outputs[].skip_export, algorithm_parameters, visualizer_config_namesremoved

Next Steps


Did this page help you?