From a9a82385bab6804d5111330367c906b842d3dee6 Mon Sep 17 00:00:00 2001 From: Keunes <11229646+keunes@users.noreply.github.com> Date: Thu, 3 Sep 2026 15:26:01 +0200 Subject: [PATCH 1/6] chore(badges): Add badges to side navigation --- astro.config.ts | 4 ++++ src/content/docs/specs/subscriptions/add-new.mdx | 3 +++ src/content/docs/specs/subscriptions/delete.mdx | 3 +++ src/content/docs/specs/subscriptions/get-all.mdx | 3 +++ src/content/docs/specs/subscriptions/get-single.mdx | 3 +++ src/content/docs/specs/subscriptions/status.mdx | 3 +++ src/content/docs/specs/subscriptions/update.mdx | 3 +++ 7 files changed, 22 insertions(+) diff --git a/astro.config.ts b/astro.config.ts index ae8e0c73..700ae999 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -43,6 +43,10 @@ export default defineConfig({ }, { label: "Subscriptions", + badge: { + text: "Core", + variant: "caution", + }, collapsed: true, items: [{ autogenerate: { diff --git a/src/content/docs/specs/subscriptions/add-new.mdx b/src/content/docs/specs/subscriptions/add-new.mdx index a94e28b8..b421d4d6 100644 --- a/src/content/docs/specs/subscriptions/add-new.mdx +++ b/src/content/docs/specs/subscriptions/add-new.mdx @@ -3,6 +3,9 @@ title: Add a new subscription description: Add a new subscription sidebar: order: 2 + badge: + text: Core + variant: caution --- import CoreAction from "@partials/_core-action.mdx"; diff --git a/src/content/docs/specs/subscriptions/delete.mdx b/src/content/docs/specs/subscriptions/delete.mdx index c9008739..4824a991 100644 --- a/src/content/docs/specs/subscriptions/delete.mdx +++ b/src/content/docs/specs/subscriptions/delete.mdx @@ -3,6 +3,9 @@ title: Delete a subscription description: Fetch the status of a deletion process sidebar: order: 6 + badge: + text: Core + variant: caution --- import CoreAction from "@partials/_core-action.mdx"; diff --git a/src/content/docs/specs/subscriptions/get-all.mdx b/src/content/docs/specs/subscriptions/get-all.mdx index 3a80a2ba..21221207 100644 --- a/src/content/docs/specs/subscriptions/get-all.mdx +++ b/src/content/docs/specs/subscriptions/get-all.mdx @@ -3,6 +3,9 @@ title: Get all subscriptions description: Get all subscriptions for a user sidebar: order: 3 + badge: + text: Core + variant: caution --- import CoreAction from "@partials/_core-action.mdx"; diff --git a/src/content/docs/specs/subscriptions/get-single.mdx b/src/content/docs/specs/subscriptions/get-single.mdx index 9a83b2e6..50360eb7 100644 --- a/src/content/docs/specs/subscriptions/get-single.mdx +++ b/src/content/docs/specs/subscriptions/get-single.mdx @@ -3,6 +3,9 @@ title: Get a single subscription description: Get a single subscription for a user sidebar: order: 4 + badge: + text: Core + variant: caution --- import CoreAction from "@partials/_core-action.mdx"; diff --git a/src/content/docs/specs/subscriptions/status.mdx b/src/content/docs/specs/subscriptions/status.mdx index d5705f51..46a25a2e 100644 --- a/src/content/docs/specs/subscriptions/status.mdx +++ b/src/content/docs/specs/subscriptions/status.mdx @@ -3,6 +3,9 @@ title: Deletion status endpoint description: Fetch the status of a deletion process sidebar: order: 7 + badge: + text: Core + variant: caution --- import CoreAction from "@partials/_core-action.mdx"; diff --git a/src/content/docs/specs/subscriptions/update.mdx b/src/content/docs/specs/subscriptions/update.mdx index 136f8375..b29ae5b8 100644 --- a/src/content/docs/specs/subscriptions/update.mdx +++ b/src/content/docs/specs/subscriptions/update.mdx @@ -3,6 +3,9 @@ title: Update a subscription description: Update details about a subscription sidebar: order: 5 + badge: + text: Core + variant: caution --- import CoreAction from "@partials/_core-action.mdx"; From 0c3519f7fbf31c467bcc3b5089abefb3d5517ada Mon Sep 17 00:00:00 2001 From: Keunes <11229646+keunes@users.noreply.github.com> Date: Thu, 3 Sep 2026 16:31:24 +0200 Subject: [PATCH 2/6] chore(badges): Make core & optional badges less obtrusive --- astro.config.ts | 3 ++- src/content/docs/specs/subscriptions/add-new.mdx | 4 ++-- src/content/docs/specs/subscriptions/delete.mdx | 4 ++-- src/content/docs/specs/subscriptions/get-all.mdx | 4 ++-- src/content/docs/specs/subscriptions/get-single.mdx | 4 ++-- src/content/docs/specs/subscriptions/status.mdx | 4 ++-- src/content/docs/specs/subscriptions/update.mdx | 4 ++-- styles/badges.css | 11 +++++++++++ 8 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 styles/badges.css diff --git a/astro.config.ts b/astro.config.ts index 700ae999..8bc2129e 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -7,6 +7,7 @@ export default defineConfig({ starlight({ title: "Open Podcast API", favicon: "favicon.ico", + customCss: ["styles/badges.css"], social: [ { icon: "github", @@ -45,7 +46,7 @@ export default defineConfig({ label: "Subscriptions", badge: { text: "Core", - variant: "caution", + class: "core", }, collapsed: true, items: [{ diff --git a/src/content/docs/specs/subscriptions/add-new.mdx b/src/content/docs/specs/subscriptions/add-new.mdx index b421d4d6..00b96873 100644 --- a/src/content/docs/specs/subscriptions/add-new.mdx +++ b/src/content/docs/specs/subscriptions/add-new.mdx @@ -4,8 +4,8 @@ description: Add a new subscription sidebar: order: 2 badge: - text: Core - variant: caution + text: C + class: core --- import CoreAction from "@partials/_core-action.mdx"; diff --git a/src/content/docs/specs/subscriptions/delete.mdx b/src/content/docs/specs/subscriptions/delete.mdx index 4824a991..fc5b85ff 100644 --- a/src/content/docs/specs/subscriptions/delete.mdx +++ b/src/content/docs/specs/subscriptions/delete.mdx @@ -4,8 +4,8 @@ description: Fetch the status of a deletion process sidebar: order: 6 badge: - text: Core - variant: caution + text: C + class: core --- import CoreAction from "@partials/_core-action.mdx"; diff --git a/src/content/docs/specs/subscriptions/get-all.mdx b/src/content/docs/specs/subscriptions/get-all.mdx index 21221207..57170124 100644 --- a/src/content/docs/specs/subscriptions/get-all.mdx +++ b/src/content/docs/specs/subscriptions/get-all.mdx @@ -4,8 +4,8 @@ description: Get all subscriptions for a user sidebar: order: 3 badge: - text: Core - variant: caution + text: C + class: core --- import CoreAction from "@partials/_core-action.mdx"; diff --git a/src/content/docs/specs/subscriptions/get-single.mdx b/src/content/docs/specs/subscriptions/get-single.mdx index 50360eb7..877ee9ed 100644 --- a/src/content/docs/specs/subscriptions/get-single.mdx +++ b/src/content/docs/specs/subscriptions/get-single.mdx @@ -4,8 +4,8 @@ description: Get a single subscription for a user sidebar: order: 4 badge: - text: Core - variant: caution + text: C + class: core --- import CoreAction from "@partials/_core-action.mdx"; diff --git a/src/content/docs/specs/subscriptions/status.mdx b/src/content/docs/specs/subscriptions/status.mdx index 46a25a2e..e7509a53 100644 --- a/src/content/docs/specs/subscriptions/status.mdx +++ b/src/content/docs/specs/subscriptions/status.mdx @@ -4,8 +4,8 @@ description: Fetch the status of a deletion process sidebar: order: 7 badge: - text: Core - variant: caution + text: C + class: core --- import CoreAction from "@partials/_core-action.mdx"; diff --git a/src/content/docs/specs/subscriptions/update.mdx b/src/content/docs/specs/subscriptions/update.mdx index b29ae5b8..f93e8d2d 100644 --- a/src/content/docs/specs/subscriptions/update.mdx +++ b/src/content/docs/specs/subscriptions/update.mdx @@ -4,8 +4,8 @@ description: Update details about a subscription sidebar: order: 5 badge: - text: Core - variant: caution + text: C + class: core --- import CoreAction from "@partials/_core-action.mdx"; diff --git a/styles/badges.css b/styles/badges.css new file mode 100644 index 00000000..38642672 --- /dev/null +++ b/styles/badges.css @@ -0,0 +1,11 @@ +.sl-badge.core { + background-color: var(--sl-color-orange-low); + border-color: var(--sl-color-orange); + color: var(--sl-color-orange-high); +} + +.sl-badge.optional { + background-color: var(--sl-color-green-low); + border-color: var(--sl-color-green); + color: var(--sl-color-green-high); +} \ No newline at end of file From 435a11676cd07fc3eb81543bbf55d5debe339d20 Mon Sep 17 00:00:00 2001 From: Keunes <11229646+keunes@users.noreply.github.com> Date: Thu, 3 Sep 2026 16:47:04 +0200 Subject: [PATCH 3/6] chore(badges): Use calmer, reassuring colors --- astro.config.ts | 2 +- styles/badges.css | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/astro.config.ts b/astro.config.ts index 8bc2129e..a4c122af 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -45,7 +45,7 @@ export default defineConfig({ { label: "Subscriptions", badge: { - text: "Core", + text: "C", class: "core", }, collapsed: true, diff --git a/styles/badges.css b/styles/badges.css index 38642672..8926fe85 100644 --- a/styles/badges.css +++ b/styles/badges.css @@ -1,11 +1,11 @@ .sl-badge.core { - background-color: var(--sl-color-orange-low); - border-color: var(--sl-color-orange); - color: var(--sl-color-orange-high); -} - -.sl-badge.optional { background-color: var(--sl-color-green-low); border-color: var(--sl-color-green); color: var(--sl-color-green-high); +} + +.sl-badge.optional { + background-color: var(--sl-color-blue-low); + border-color: var(--sl-color-blue); + color: var(--sl-color-blue-high); } \ No newline at end of file From a70c389733ac502f0e5df49bfa779363f1b98dc5 Mon Sep 17 00:00:00 2001 From: Keunes <11229646+keunes@users.noreply.github.com> Date: Thu, 3 Sep 2026 17:32:17 +0200 Subject: [PATCH 4/6] chore(apiClassification): Replace API classification custom partials with banners --- astro.config.ts | 5 +++- src/components/Banner.astro | 23 +++++++++++++++++++ src/content.config.ts | 15 +++++++++++- .../docs/specs/subscriptions/add-new.mdx | 4 +--- .../docs/specs/subscriptions/delete.mdx | 4 +--- .../docs/specs/subscriptions/get-all.mdx | 4 +--- .../docs/specs/subscriptions/get-single.mdx | 4 +--- .../docs/specs/subscriptions/index.mdx | 5 +--- .../docs/specs/subscriptions/status.mdx | 4 +--- .../docs/specs/subscriptions/update.mdx | 4 +--- src/data/banners.ts | 6 +++++ styles/API-classification.css | 22 ++++++++++++++++++ styles/badges.css | 11 --------- 13 files changed, 76 insertions(+), 35 deletions(-) create mode 100644 src/components/Banner.astro create mode 100644 src/data/banners.ts create mode 100644 styles/API-classification.css delete mode 100644 styles/badges.css diff --git a/astro.config.ts b/astro.config.ts index a4c122af..1183782b 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -7,7 +7,10 @@ export default defineConfig({ starlight({ title: "Open Podcast API", favicon: "favicon.ico", - customCss: ["styles/badges.css"], + customCss: ["styles/API-classification.css"], + components: { + Banner: "./src/components/Banner.astro", + }, social: [ { icon: "github", diff --git a/src/components/Banner.astro b/src/components/Banner.astro new file mode 100644 index 00000000..af806c6d --- /dev/null +++ b/src/components/Banner.astro @@ -0,0 +1,23 @@ +--- +import { apiClassificationBanners } from '../data/banners'; + +const { apiClassification } = Astro.locals.starlightRoute.entry.data; +const content = apiClassification ? apiClassificationBanners[apiClassification] : undefined; +--- + +{content &&
{content}
} + + \ No newline at end of file diff --git a/src/content.config.ts b/src/content.config.ts index 1e3e4cf9..93d250ce 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -1,8 +1,21 @@ import { defineCollection } from 'astro:content'; +import { z } from 'astro/zod'; import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'; import { docsLoader } from '@astrojs/starlight/loaders'; export const collections = { - docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), + docs: defineCollection({ + loader: docsLoader(), + schema: docsSchema({ + extend: z.object({ + apiClassification: z.enum([ + 'core-endpoint', + 'optional-endpoint', + 'core-action', + 'optional-action', + ]).optional(), + }), + }), + }), i18n: defineCollection({ type: 'data', schema: i18nSchema() }), }; diff --git a/src/content/docs/specs/subscriptions/add-new.mdx b/src/content/docs/specs/subscriptions/add-new.mdx index 00b96873..d57c4cd9 100644 --- a/src/content/docs/specs/subscriptions/add-new.mdx +++ b/src/content/docs/specs/subscriptions/add-new.mdx @@ -6,13 +6,11 @@ sidebar: badge: text: C class: core +apiClassification: core-endpoint --- -import CoreAction from "@partials/_core-action.mdx"; import { Tabs, TabItem } from '@astrojs/starlight/components'; - - ```http title="Endpoint" POST /v1/subscriptions ``` diff --git a/src/content/docs/specs/subscriptions/delete.mdx b/src/content/docs/specs/subscriptions/delete.mdx index fc5b85ff..7cdb9b56 100644 --- a/src/content/docs/specs/subscriptions/delete.mdx +++ b/src/content/docs/specs/subscriptions/delete.mdx @@ -6,13 +6,11 @@ sidebar: badge: text: C class: core +apiClassification: core-action --- -import CoreAction from "@partials/_core-action.mdx"; import { Tabs, TabItem } from '@astrojs/starlight/components'; - - ```http title="Endpoint" DELETE /v1/subscriptions/{guid} ``` diff --git a/src/content/docs/specs/subscriptions/get-all.mdx b/src/content/docs/specs/subscriptions/get-all.mdx index 57170124..6ae17172 100644 --- a/src/content/docs/specs/subscriptions/get-all.mdx +++ b/src/content/docs/specs/subscriptions/get-all.mdx @@ -6,13 +6,11 @@ sidebar: badge: text: C class: core +apiClassification: core-action --- -import CoreAction from "@partials/_core-action.mdx"; import { Tabs, TabItem } from '@astrojs/starlight/components'; - - ```http title="Endpoint" GET /v1/subscriptions ``` diff --git a/src/content/docs/specs/subscriptions/get-single.mdx b/src/content/docs/specs/subscriptions/get-single.mdx index 877ee9ed..528cc22c 100644 --- a/src/content/docs/specs/subscriptions/get-single.mdx +++ b/src/content/docs/specs/subscriptions/get-single.mdx @@ -6,13 +6,11 @@ sidebar: badge: text: C class: core +apiClassification: core-action --- -import CoreAction from "@partials/_core-action.mdx"; import { Tabs, TabItem } from '@astrojs/starlight/components'; - - ```http title="Endpoint" GET /v1/subscriptions/{guid} ``` diff --git a/src/content/docs/specs/subscriptions/index.mdx b/src/content/docs/specs/subscriptions/index.mdx index a6975902..5aee22eb 100644 --- a/src/content/docs/specs/subscriptions/index.mdx +++ b/src/content/docs/specs/subscriptions/index.mdx @@ -5,12 +5,9 @@ prev: false sidebar: label: Overview order: 1 +apiClassification: core-endpoint --- -import CoreEndpoint from "@partials/_core-endpoint.mdx"; - - - The subscriptions endpoint is used to synchronize subscriptions between a server and connected clients. The server is treated as the authoritative source for subscription information. Clients can query the endpoint by specifying the datetime from which they want to fetch changes to ensure they only fetch information that is relevant to them since their last sync. Subscriptions represent the feeds a user has subscribed to. A subscription object stores essential information about each subscription and acts as an index that links other activity information together. diff --git a/src/content/docs/specs/subscriptions/status.mdx b/src/content/docs/specs/subscriptions/status.mdx index e7509a53..9f98732d 100644 --- a/src/content/docs/specs/subscriptions/status.mdx +++ b/src/content/docs/specs/subscriptions/status.mdx @@ -6,13 +6,11 @@ sidebar: badge: text: C class: core +apiClassification: core-action --- -import CoreAction from "@partials/_core-action.mdx"; import { Tabs, TabItem } from '@astrojs/starlight/components'; - - ```http title="Endpoint" GET /v1/deletions/{id} ``` diff --git a/src/content/docs/specs/subscriptions/update.mdx b/src/content/docs/specs/subscriptions/update.mdx index f93e8d2d..9f1d71c8 100644 --- a/src/content/docs/specs/subscriptions/update.mdx +++ b/src/content/docs/specs/subscriptions/update.mdx @@ -6,13 +6,11 @@ sidebar: badge: text: C class: core +apiClassification: core-action --- -import CoreAction from "@partials/_core-action.mdx"; import { Tabs, TabItem } from '@astrojs/starlight/components'; - - ```http title="Endpoint" PATCH /v1/subscriptions/{guid} ``` diff --git a/src/data/banners.ts b/src/data/banners.ts new file mode 100644 index 00000000..7cf495e0 --- /dev/null +++ b/src/data/banners.ts @@ -0,0 +1,6 @@ +export const apiClassificationBanners = { + 'core-endpoint': 'This is a core endpoint. All implementing servers and clients MUST support it.', + 'optional-endpoint': 'This is an optional endpoint.', + 'core-action': 'This is a core action. All implementing servers and clients MUST support it.', + 'optional-action': 'This is an optional action.', +} as const; \ No newline at end of file diff --git a/styles/API-classification.css b/styles/API-classification.css new file mode 100644 index 00000000..e61d3a93 --- /dev/null +++ b/styles/API-classification.css @@ -0,0 +1,22 @@ +.sl-badge.core, +.sl-banner.core-endpoint, +.sl-banner.core-action { + background-color: var(--sl-color-green-low); + border-color: var(--sl-color-green); + color: var(--sl-color-green-high); +} + +.sl-badge.optional, +.sl-banner.optional-endpoint, +.sl-banner.optional-action { + background-color: var(--sl-color-blue-low); + border-color: var(--sl-color-blue); + color: var(--sl-color-blue-high); +} + +.sl-banner.core-endpoint, +.sl-banner.core-action, +.sl-banner.optional-endpoint, +.sl-banner.optional-action { + box-shadow: none; +} \ No newline at end of file diff --git a/styles/badges.css b/styles/badges.css deleted file mode 100644 index 8926fe85..00000000 --- a/styles/badges.css +++ /dev/null @@ -1,11 +0,0 @@ -.sl-badge.core { - background-color: var(--sl-color-green-low); - border-color: var(--sl-color-green); - color: var(--sl-color-green-high); -} - -.sl-badge.optional { - background-color: var(--sl-color-blue-low); - border-color: var(--sl-color-blue); - color: var(--sl-color-blue-high); -} \ No newline at end of file From 566fb8b131dfee48d0392c6cabae866dd19f8d90 Mon Sep 17 00:00:00 2001 From: Keunes <11229646+keunes@users.noreply.github.com> Date: Thu, 3 Sep 2026 18:27:40 +0200 Subject: [PATCH 5/6] feat(conventions): Add dedicated conventions section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: CiarĂ¡n Ainsworth --- astro.config.ts | 9 ++++++ .../docs/specs/conventions/client-ids.md | 30 +++++++++++++++++++ .../core-and-optional-functionality.md | 13 ++++++++ src/content/docs/specs/conventions/index.md | 21 +++++++++++++ .../docs/specs/conventions/requests.md | 14 +++++++++ .../docs/specs/conventions/responses.md | 21 +++++++++++++ .../conventions/synchronization-model.md | 24 +++++++++++++++ .../timestamps-and-modifications.md | 30 +++++++++++++++++++ 8 files changed, 162 insertions(+) create mode 100644 src/content/docs/specs/conventions/client-ids.md create mode 100644 src/content/docs/specs/conventions/core-and-optional-functionality.md create mode 100644 src/content/docs/specs/conventions/index.md create mode 100644 src/content/docs/specs/conventions/requests.md create mode 100644 src/content/docs/specs/conventions/responses.md create mode 100644 src/content/docs/specs/conventions/synchronization-model.md create mode 100644 src/content/docs/specs/conventions/timestamps-and-modifications.md diff --git a/astro.config.ts b/astro.config.ts index 1183782b..0da02cfd 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -45,6 +45,15 @@ export default defineConfig({ label: "Introduction", link: "specs", }, + { + label: "Conventions", + collapsed: true, + items: [{ + autogenerate: { + directory: "specs/conventions", + } + }], + }, { label: "Subscriptions", badge: { diff --git a/src/content/docs/specs/conventions/client-ids.md b/src/content/docs/specs/conventions/client-ids.md new file mode 100644 index 00000000..7def705c --- /dev/null +++ b/src/content/docs/specs/conventions/client-ids.md @@ -0,0 +1,30 @@ +--- +title: Client IDs +description: Conventions for identifying clients that interact with the Open Podcast API. +sidebar: + order: 4 +--- + +Each client must generate and persist a [UUIDv4](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-4) value that uniquely identifies that client installation or instance. Client IDs identify a client installation rather than a user or device. + +Include this value with every API request using the `Client-ID` header. + +```txt +Client-ID: 550e8400-e29b-41d4-a716-446655440000 +``` + +:::note +The client ID is used only to identify the source of synchronized changes. It is not an authentication credential and can be freely regenerated if the client state is reset. +::: + +## Requirements + +* Generate the ID once, when the client is first initialized. +* The ID must be a valid UUID version 4 (random UUID). +* Persist the ID and reuse it for all future requests. +* Do not generate a new ID for each request. +* Each independent client installation or instance should have its own unique ID. + +## Purpose + +The server uses the client ID to identify the origin of changes. Endpoints that return updates can exclude changes created by the requesting client, allowing clients to synchronize only updates originating from other clients. \ No newline at end of file diff --git a/src/content/docs/specs/conventions/core-and-optional-functionality.md b/src/content/docs/specs/conventions/core-and-optional-functionality.md new file mode 100644 index 00000000..cc8735e3 --- /dev/null +++ b/src/content/docs/specs/conventions/core-and-optional-functionality.md @@ -0,0 +1,13 @@ +--- +title: Core and optional functionality +description: Conventions for distinguishing core and optional functionality in the Open Podcast API. +sidebar: + order: 3 +--- + +To ensure that the end-user experience is consistent across implementations, the specifications mark endpoints and features as Core (required) and Optional. + +* Core: The feature or endpoint MUST be supported by all clients and servers. +* Optional: The feature or endpoint is considered to be additional functionality. + +Clients and servers may optionally support any combination of these features. Any project implementing Optional functionality should inform users about what is supported. \ No newline at end of file diff --git a/src/content/docs/specs/conventions/index.md b/src/content/docs/specs/conventions/index.md new file mode 100644 index 00000000..aa8ecb4b --- /dev/null +++ b/src/content/docs/specs/conventions/index.md @@ -0,0 +1,21 @@ +--- +title: Open Podcast API conventions +description: Practices and policies that apply to the full Open Podcast API. +prev: false +sidebar: + label: Overview + order: 1 +--- + +The following conventions are used for all endpoints in the Open Podcast API specification: +* Core and optional functionality +* Synchronization model + * Client behavior + * Server behavior +* Client IDs +* Timestamps & modifications +* Requests + * Content type +* Responses + * Response codes + * Pagination \ No newline at end of file diff --git a/src/content/docs/specs/conventions/requests.md b/src/content/docs/specs/conventions/requests.md new file mode 100644 index 00000000..c1b5a0e3 --- /dev/null +++ b/src/content/docs/specs/conventions/requests.md @@ -0,0 +1,14 @@ +--- +title: Requests +description: Conventions for requests made to the Open Podcast API. +sidebar: + order: 6 +--- + +## Content type + +All endpoints covered by the Open Podcast API require a `Content-Type` header to request JSON information from the server. + +```txt +Content-Type: application/json +``` \ No newline at end of file diff --git a/src/content/docs/specs/conventions/responses.md b/src/content/docs/specs/conventions/responses.md new file mode 100644 index 00000000..b846cd85 --- /dev/null +++ b/src/content/docs/specs/conventions/responses.md @@ -0,0 +1,21 @@ +--- +title: Responses +description: Conventions for responses returned by the Open Podcast API. +sidebar: + order: 7 +--- + +## Response codes + +The Open Podcast API uses the following HTTP response codes: + +* `200`: operation completed successfully. +* `400`: the client sent an invalid request. +* `401`: the client attempted to request a resource without authentication. +* `403`: the client requested access to a resource it does not have permission to interact with. +* `404`: the client attempted to access a non-existent resource. +* `500`: the server encountered an error while performing a request. + +## Pagination + +All bulk `GET` requests must be paginated using a stable offset value. For the sync endpoint, this value must be a UTC timestamp in the RFC3339 format. For bulk entity endpoints, the entity ID or another stable incremental value should be used. The server must respond with navigation links in each response to point clients to the next and previous pages of results. \ No newline at end of file diff --git a/src/content/docs/specs/conventions/synchronization-model.md b/src/content/docs/specs/conventions/synchronization-model.md new file mode 100644 index 00000000..ef531282 --- /dev/null +++ b/src/content/docs/specs/conventions/synchronization-model.md @@ -0,0 +1,24 @@ +--- +title: Synchronization model +description: Conventions for synchronizing data between Open Podcast API clients and servers. +sidebar: + order: 2 +--- + +The Open Podcast API follows a batch synchronization model through a [synchronization endpoint](/specs/sync). Updates are processed in bulk using action names and entity types as discriminators. To reduce the risk of performance degradation while facilitating bulk updates from clients, updates are limited to 30 actions per request. + +## Client behavior + +Synchronization of data is based on client timestamps. These are considered authoritative. When a user performs an action on a client, the client must record the UTC timestamp of the action and send it with the corresponding update action. + +The client may send multiple bulk requests in sequence, starting from the earliest recorded timestamp. Requests are expected to be sent in chronological order. + +## Server behavior + +The server modifies state based on a last-write-wins model where the action with the latest timestamp always applies. + +The server must disregard any actions with a timestamp earlier than the current modification timestamp as stale. The server must always return the current canonical state of the requested entity whether the action was applied or not. + +Clients may request information from a given offset timestamp to fetch all relevant updates that have occurred since their last sync. Servers must respond only with entities that have been updated since the offset timestamp. The [client ID](../client-ids) of the client that made the most recent change to an entity must be preserved and only updates not made by the requesting client should be returned. + +In addition to the central sync endpoint, all entities must be made available for retrieval from domain-specific endpoints. Servers must provide bulk and single-item endpoints for each entity type. diff --git a/src/content/docs/specs/conventions/timestamps-and-modifications.md b/src/content/docs/specs/conventions/timestamps-and-modifications.md new file mode 100644 index 00000000..1c8a96d1 --- /dev/null +++ b/src/content/docs/specs/conventions/timestamps-and-modifications.md @@ -0,0 +1,30 @@ +--- +title: Timestamps & modifications +description: Conventions for timestamps and tracking modifications in the Open Podcast API. +sidebar: + order: 5 +--- + +## Timestamps + +:::caution[Important] +The synchronization protocol assumes that client clocks are reasonably accurate. Devices with significantly incorrect clocks may observe unexpected synchronization order. +::: + +All actions sent to the server must include a UTC timestamp recorded by the client. These timestamps determine action order. The server compares the client-supplied timestamp with the entity's current modification timestamp. An action with a later timestamp MUST be applied. An action with an earlier timestamp MUST be ignored. + +Timestamps must be submitted in the [RFC3339 format](https://www.rfc-editor.org/rfc/rfc3339). Servers must accept any fractional precision. For example: + +* `2026-07-11T15:30:00Z` +* `2026-07-11T15:30:00.1Z` +* `2026-07-11T15:30:00.123456Z` + +Once generated, an action's timestamp must not be modified. Retried requests must preserve the original timestamp. + +Actions contained within a batch are independent. Servers must order actions solely according to their timestamps rather than the order in which they appear in the request. + +An action is applied only if its timestamp is strictly later than the entity's current `last_updated` timestamp. Actions with an earlier or identical timestamp are ignored. + +## Modifications + +For each entity, servers must keep track of the `last_updated` timestamp and associated requesting client ID when a change is made to the entity metadata. When a client requests a sync response, any entity that has been modified by a different client must be returned. \ No newline at end of file From 1f347633027eb905f9cc908e810de6b0c9f75f88 Mon Sep 17 00:00:00 2001 From: Keunes <11229646+keunes@users.noreply.github.com> Date: Mon, 7 Sep 2026 09:50:15 +0200 Subject: [PATCH 6/6] feat(badges): Add badges to Conventions explanation --- .../core-and-optional-functionality.md | 13 ------------- .../core-and-optional-functionality.mdx | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 13 deletions(-) delete mode 100644 src/content/docs/specs/conventions/core-and-optional-functionality.md create mode 100644 src/content/docs/specs/conventions/core-and-optional-functionality.mdx diff --git a/src/content/docs/specs/conventions/core-and-optional-functionality.md b/src/content/docs/specs/conventions/core-and-optional-functionality.md deleted file mode 100644 index cc8735e3..00000000 --- a/src/content/docs/specs/conventions/core-and-optional-functionality.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: Core and optional functionality -description: Conventions for distinguishing core and optional functionality in the Open Podcast API. -sidebar: - order: 3 ---- - -To ensure that the end-user experience is consistent across implementations, the specifications mark endpoints and features as Core (required) and Optional. - -* Core: The feature or endpoint MUST be supported by all clients and servers. -* Optional: The feature or endpoint is considered to be additional functionality. - -Clients and servers may optionally support any combination of these features. Any project implementing Optional functionality should inform users about what is supported. \ No newline at end of file diff --git a/src/content/docs/specs/conventions/core-and-optional-functionality.mdx b/src/content/docs/specs/conventions/core-and-optional-functionality.mdx new file mode 100644 index 00000000..d9648458 --- /dev/null +++ b/src/content/docs/specs/conventions/core-and-optional-functionality.mdx @@ -0,0 +1,15 @@ +--- +title: Core and optional functionality +description: Conventions for distinguishing core and optional functionality in the Open Podcast API. +sidebar: + order: 3 +--- + +import { Badge } from '@astrojs/starlight/components'; + +To ensure that the end-user experience is consistent across implementations, the specifications mark endpoints and features as (required) and . + +* : The feature or endpoint MUST be supported by all clients and servers. +* : The feature or endpoint is considered to be additional functionality. + +Clients and servers may optionally support any combination of these features. Any project implementing Optional functionality should inform users about what is supported. \ No newline at end of file