Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ For a roadmap including expected timeline, please refer to [ROADMAP.md](./ROADMA

## [unreleased]

### Added

- Added **Push Transport** mode documentation and schema support
- Push transport allows ORD providers to push ORD documents directly to aggregators via HTTP POST
- Introduced `definitions` property on ORD Document for embedding resource definitions inline
- The key is the URL path (matching `resourceDefinitions[].url`), the value is the raw content as a string
- Content is treated as an opaque text blob, preserving original formatting for all formats (OpenAPI, AsyncAPI, WSDL, etc.)
- Added `embedded` access strategy type for resource definitions that are inline rather than fetched
- Added [embedded access strategy documentation](docs/spec-extensions/access-strategies/embedded.md)
- Added [push transport example document](examples/documents/document-push-transport.json)
- Added draft proposal for [Aggregator Configuration](docs/spec-v1/concepts/aggregator-configuration.md) including validation results endpoint concept

## [1.14.0]

### Added
Expand Down
95 changes: 95 additions & 0 deletions docs/spec-extensions/access-strategies/embedded.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
title: Embedded Access Strategy
description: Resource definition is embedded inline in the ORD document (push transport).
sidebar_position: 1
---

# Embedded Access Strategy

> **Status**: Draft Proposal (WIP)
>
> This access strategy is part of the [Push Transport](../../spec-v1/index.md#push-transport) proposal.

## Description

The `embedded` access strategy indicates that the resource definition content is provided inline within the ORD document itself, rather than being fetched from an external URL.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why inline instead of a ZIP file?

Embedded resources could easily push the document past the 2MB limit. Also it may greatly increase the memory requirements for parsing the document.

Example:

/
- MANIFEST.MF
- resource-1.ord.json
- resource-M.ord.json
- api-123.openapi.json
- api-XYZ.openapi.json

With a zip file you can push an arbitrary amount of resource definitions (and even ORD documents if it makes sense).

All files should be listed in the manifest. It acts as the document served at the "well-known" endpoint.

Optionally it can contain signatures, hashes, etc.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I also thought about it. It would be a different approach, then we don't need embedding. Instead we use relative file links.

Those ZIP files could get quite big though and I'm not sure how ideal it is with regards to HTTP protocol which already does compression as part of the transport.

I see another alternative: POST/PUT all the files as part of a transaction, which then can be committed via separate action on the API. Then there can be a GET on the transaction to also receive feedback.
This would be closer to HTTP conventions and reuse of how the protocol works, also would be more async. It requires some transaction management on aggregator side, though.


This is specifically designed for [push transport](../../spec-v1/index.md#push-transport) scenarios where:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does "designed for" mean "limited to"? Embedded resources seem equally useful in pull mode — a provider might want to serve a self-contained ORD document without requiring the aggregator to make additional HTTP requests for resource definitions. IMO push vs. pull is purely a transport concern and shouldn't dictate what the document can contain.


- The ORD provider pushes the complete ORD document including all resource definitions to an aggregator
- The aggregator does not need to make additional requests to fetch resource definitions
- All metadata is self-contained in a single push request

The `accessStrategy`.`type` value for it is: `embedded`.

## How It Works

When a `resourceDefinition` uses the `embedded` access strategy:

1. The `url` field still contains the logical path/identifier for the resource definition
2. The actual content is provided in the document-level `definitions` property
3. The `url` value is used as the key to look up the content in `definitions`

### Example

```json
{
"openResourceDiscovery": "1.15",
"apiResources": [
{
"ordId": "sap.foo:apiResource:myApi:v1",
"resourceDefinitions": [
{
"type": "openapi-v3",
"mediaType": "application/json",
"url": "/api/my-api/openapi.json",
"accessStrategies": [
{
"type": "embedded"
}
]
}
]
}
],
"definitions": {
"/api/my-api/openapi.json": "{\"openapi\":\"3.0.0\",\"info\":{\"title\":\"My API\",\"version\":\"1.0.0\"},\"paths\":{}}"
}
}
```

## Provider Implementation

The ORD provider MUST:

- Set `accessStrategies[].type` to `embedded` for any resource definition whose content is included inline
- Include the resource definition content in the top-level `definitions` property
- Use the same URL path as the key in `definitions` that is referenced in `resourceDefinitions[].url`
- Encode the content as a string (preserving original formatting)

## Aggregator / Consumer Implementation

The ORD aggregator or consumer MUST:

- Recognize `embedded` as an access strategy type
- Look up the content in the `definitions` property using the `url` as the key
- NOT attempt to fetch the URL externally when `embedded` is specified
- Parse the string content according to the `mediaType` specified

## When to Use

Use the `embedded` access strategy when:

- Pushing ORD documents to an aggregator (push transport)
- Integrating ORD publishing into CI/CD pipelines
- The provider cannot or does not want to host resource definitions at accessible URLs
- You want to ensure atomic updates of metadata and definitions together

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should atomic "all-or-nothing" semantics be a hard requirement here? If a push partially fails (e.g. some resource definitions are invalid), should the aggregator reject the entire payload or accept the valid parts?

This needs an explicit statement in the spec — partial updates could leave an aggregator in an inconsistent state, but strict atomicity may be impractical for large payloads.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, so far we left it to the aggregator as an implementation detail. I think there's some "oppinion" in how strong this is fulfilled.

I'll keep it in mind, but defining this would be probably a general enhancement, not just for the push based appraoch.


## Comparison with `open`

| Aspect | `open` | `embedded` |
|--------|--------|------------|
| Content location | External URL | Inline in document |
| Fetch required | Yes | No |
| Transport mode | Pull | Push |
| Self-contained | No | Yes |
190 changes: 182 additions & 8 deletions docs/spec-v1/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,18 +193,192 @@ Manual import of the [ORD document](#ord-document) as a JSON file into an intere
- The ORD document alone is sufficient for this type of consumption
- All URLs in the document MUST be resolvable (e.g. through `baseUrl` or full URLs)

#### Push Transport
### Push Transport

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This proposal feels underspecified for enabling reusable, interoperable aggregators. For anyone to build a generic aggregator that works across providers, there needs to be a standard push protocol — not just a payload shape. Concretely, that means defining:

  • A standard push endpoint (or a standard discovery mechanism for it, e.g. a well-known)
  • Transport mode — multipart, streaming, ZIP, plain JSON body? This matters for the 2MB-per-document constraint: embedding resource definitions inline as strings inside a JSON payload doesn't scale, and the choice of transport directly determines whether large payloads are even feasible
  • Sync vs. async communication — I'd strongly advocate for async here. A fire-and-forget push with a callback/webhook/polling for validation results would allow providers to push arbitrarily large amounts of data without blocking the CI/CD pipeline on aggregator processing time. Sync push only works for small, fast payloads; async is the only model that scales

Without these being standardized, every aggregator will implement its own variant and the push mechanism won't be reusable — which defeats the purpose.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's on point. Right now it's underdefined, reason is that we would need to build / prototype this along with defining here in the spec and that hasn't happened yet. I agree that those questions need to be clarified.

On the async side, it comes with some drawbacks like not getting errors / problems quick enough. But since the aggregator needs a bit time to validate, maybe it's more realistic to batch more and then also receive validation asynchronously later. It would be a more complicated pattern than just sync push with direct error feedback.


In push transport mode, [ORD information](#ord-information) is pushed directly to an [ORD aggregator](#ord-aggregator) via HTTP POST requests.
This mode eliminates the need for an [ORD Provider](#ord-provider) to implement the [ORD Provider API](#ord-provider-api) with its configuration and document endpoints.

Push transport is particularly suitable for:

- Static metadata that is known at design-time or deploy-time
- CI/CD pipeline integration where metadata is pushed as part of the build/deployment process
- Providers that cannot or prefer not to host a runtime ORD Provider API

##### Push Transport - Pros

- No need to implement and host an ORD Provider API (simpler provider implementation)
- Can be integrated into CI/CD pipelines (design-time or deploy-time)
- Configuration or extensibility changes are pushed immediately when they occur (no polling delay)
- Direct feedback channel for validation errors from the aggregator: validation issues can be returned as part of the push response, making it easier for providers to detect and fix problems immediately (compared to pull where issues may go unnoticed)
- More efficient for tenant-specific (system-instance-aware) metadata: the provider knows exactly when changes occur and can push updates selectively, avoiding the need for aggregators to poll all tenants repeatedly

##### Push Transport - Cons

- Every provider needs to know where to push (aggregator endpoint must be known)
- Provider must actively push updates (compared to passive pull)
- Additional authentication/authorization setup between provider and aggregator
- Centralized approach (aggregator must be available to receive pushes)

##### Push Transport Implementation

###### ORD Push Document

For push transport, the standard [ORD document](#ord-document) format is used with one addition: a `definitions` property that allows inline [resource definitions](#resource-definition).

When using pull transport, resource definitions are referenced via URLs and fetched separately by the aggregator.
In push transport, these definitions can be provided inline within the ORD document itself using the `definitions` property.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to limit this to push transport. Most aggregator implementations will have a single document-handling pipeline regardless of how the document arrived — the transport layer should be transparent to the processing logic. Artificially restricting embedded resources to push mode adds complexity for no benefit.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, it would also make sense on pull.
Where I'm not sure: It would be a new feature not every consumer would support, so it's an incompatible change if we backport it to pull transport. So it feels a bit dangerous to introduce it to pull without having a major release (which I'd like to avoid).

Conceptually, it would of course work for both transport modes.


When embedding definitions, the resource definition's access strategy SHOULD be set to [`embedded`](../spec-extensions/access-strategies/embedded.md) to explicitly indicate that the content is inline rather than fetched from a URL.

The `definitions` property is a dictionary where:
- The **key** is the URL path (as referenced by resources via `resourceDefinitions[].url`)
- The **value** is the raw content of the resource definition as a **string**

The content is treated as an opaque text blob, preserving original formatting and whitespace.
This works uniformly for all definition formats (OpenAPI JSON/YAML, AsyncAPI, WSDL, JSON Schema, etc.).

This enables the aggregator to correlate inline definitions with the resources that reference them, keeping all metadata self-contained in a single push request.

Example structure:
```json
{
"openResourceDiscovery": "1.14",
"describedSystemInstance": {
"baseUrl": "https://example.com"
},
"apiResources": [
{
"ordId": "sap.example:apiResource:my-api:v1",
"resourceDefinitions": [
{
"type": "openapi-v3",
"url": "/api/my-api/openapi.json",
"mediaType": "application/json",
"accessStrategies": [
{ "type": "embedded" }
]
}
]
}
],
"definitions": {
"/api/my-api/openapi.json": "{\"openapi\":\"3.0.0\",\"info\":{\"title\":\"My API\",\"version\":\"1.0.0\"}}"
}
}
```

> 🚧 The specification currently does not cover this mode.
###### ORD Aggregator Push API

An [ORD aggregator](#ord-aggregator) that supports push transport MUST provide a dedicated push API endpoint for receiving ORD documents.

<!-- The aggregator MAY advertise its push API capabilities via its own ORD configuration endpoint at `/.well-known/open-resource-discovery`.
The aggregator configuration MAY include:
- The URL of the push API endpoint
- Supported access strategies for authenticating providers
- Description and documentation links for onboarding


Example aggregator configuration:
```json
{
"openResourceDiscoveryAggregator": {
"supportedVersions": ["1.14", "1.15"],
"supportedTransportModes": {
"push": {
"publishDocumentEndpoint": "/ord-publishing-api/v1/documents",
"validationResultsEndpoint": "/ord-publishing-api/v1/validationResults",
"accessStrategies": [
{
"type": "sap:oauth-client-credentials:v1",
"description": "Contact ord-support@example.com to request push credentials.",
"documentationLink": "https://help.example.com/ord/push-onboarding"
}
]
}
}
}
}
``` -->

###### Push API Contract

The push API endpoint MUST:
- Accept HTTP `POST` requests with `Content-Type: application/json`
- Expect the request body to be a valid [ORD document](#ord-document)
- Support the `definitions` property for inline resource definitions
- Return appropriate HTTP status codes:
- `200 OK` or `201 Created` on success
- `400 Bad Request` for any client error (malformed JSON, invalid ORD document, validation failures). Details SHOULD be provided in the response body.
- `401 Unauthorized` or `403 Forbidden` for authentication/authorization failures
- **TODO**: The exact response format for validation errors is not yet formally specified, it's up to the aggregator to define it.

Example request:
```http
POST /ord/v1/push HTTP/1.1
Host: aggregator.example.com
Content-Type: application/json
Authorization: Bearer <token>

{
"openResourceDiscovery": "1.14",
"describedSystemInstance": { ... },
"apiResources": [ ... ],
"definitions": { ... }
}
```

###### Validation Results Endpoint (Draft)

> **Status**: Draft Proposal - See [Aggregator Configuration (Draft)](./concepts/aggregator-configuration.md) for details.

Some validations can only be performed after the aggregator has processed multiple documents (e.g., cross-document reference checks, namespace consistency).
To support deferred validation feedback, aggregators MAY provide a validation results endpoint where providers can retrieve errors, warnings, and info messages.

This enables:
- Async validation that doesn't block the push request
- Aggregated feedback across multiple pushed documents
- Scoped queries by namespace or package

###### Provider Authorization

The aggregator MUST ensure that providers can only push content they are authorized to manage.
Authorization rules depend on the aggregator implementation but typically include:
- Validating that the provider is allowed to describe the claimed [system instance](#system-instance), e.g. by verifying ownership of the `baseUrl` and/or other identifiers in the ORD document
- Validating that the provider owns the [namespaces](#namespace) used in the pushed ORD IDs
- Validating that the push credentials match the expected provider identity

###### Push Transport Sequence Diagram

```mermaid
sequenceDiagram
participant Provider as ORD Provider
participant CI as CI/CD Pipeline
participant Aggregator as ORD Aggregator

Provider->>CI: Trigger build/deployment
CI->>CI: Generate ORD document with inline definitions
CI->>Aggregator: POST ORD document (using access strategy)
Aggregator->>Aggregator: Validate document and authorization
Aggregator-->>CI: Response (success/errors)

opt On validation errors
CI->>CI: Handle errors, notify developers
end
```

The Document can be pushed to the interested ORD aggregator, e.g. via a webhook, a known HTTP POST endpoint, or via file upload.
#### Other Modes of Transport

- Every system instance needs to know where the ORD documents need to be pushed to.
- An ORD aggregator might provide a dedicated HTTP POST endpoint for this.
- Changes can be pushed faster and more efficiently compared to the [pull transport](#pull-transport).
- The specification currently does not cover this mode.
Other modes of transport have not yet been standardized/specified.
They are only listed here to outline potential modes that we anticipate.

##### Import Transport

Manual import of the [ORD document](#ord-document) as a JSON file into an interested system or tool (offline mode):

- The system instances do not need to know each other or be integrated in any way
- The ORD document alone is sufficient for this type of consumption, it should include all necessary information and definitions inline (via the `definitions` property as described in the push transport section)

#### Event-Driven Transport
##### Event-Driven Transport

> 🚧 The specification currently does not cover this mode.

Expand Down
47 changes: 47 additions & 0 deletions examples/documents/document-push-transport.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"$schema": "https://open-resource-discovery.org/spec-v1/interfaces/Document.schema.json",
"openResourceDiscovery": "1.14",
"description": "Minimal example for push transport with inline resource definition",
"describedSystemInstance": {
"baseUrl": "https://example.com"
},
"packages": [
{
"ordId": "sap.example:package:push-demo:v1",
"title": "Push Transport Demo",
"shortDescription": "Demonstrates push transport with inline definitions",
"description": "This package demonstrates push transport with inline resource definitions.",
"version": "1.0.0",
"vendor": "sap:vendor:SAP:"
}
],
"apiResources": [
{
"ordId": "sap.example:apiResource:hello-api:v1",
"title": "Hello API",
"shortDescription": "Simple greeting API",
"description": "A simple greeting API that demonstrates push transport.",
"version": "1.0.0",
"visibility": "public",
"releaseStatus": "active",
"partOfPackage": "sap.example:package:push-demo:v1",
"apiProtocol": "rest",
"resourceDefinitions": [
{
"type": "openapi-v3",
"mediaType": "application/json",
"url": "/api/hello/openapi.json",
"accessStrategies": [
{
"type": "embedded"
}
]
}
],
"entryPoints": ["/api/hello"]
}
],
"definitions": {
"/api/hello/openapi.json": "{\"openapi\":\"3.0.3\",\"info\":{\"title\":\"Hello API\",\"version\":\"1.0.0\"},\"paths\":{\"/hello\":{\"get\":{\"summary\":\"Get greeting\",\"responses\":{\"200\":{\"description\":\"A greeting message\",\"content\":{\"application/json\":{\"schema\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\"}}}}}}}}}}}"
}
}
Loading
Loading