Skip to main content

max / makenotwork

openapi: document the SyncKit rotation and subscription endpoints (audit Run 20 Phase 4) sync.rs annotated 19 handlers but openapi.rs listed only 8, so key rotation (begin/entries/batch/complete/cancel) and subscription/account (account, subscription status, app pricing, quote, checkout, storage-cap) were absent from the published spec — invisible to any SDK generator despite being fully annotated. These are public SyncKit SDK surface, so document them: add the 11 paths and their 16 schemas. A drift guard test asserts the spec keeps them. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-03 07:36 UTC
Commit: d7432e55f9a428837fa4e9db480f8e723803b849
Parent: 0a4010b
1 file changed, +61 insertions, -0 deletions
@@ -38,6 +38,19 @@ use utoipa::OpenApi;
38 38 crate::routes::synckit::sync::delete_device,
39 39 crate::routes::synckit::sync::put_sync_key,
40 40 crate::routes::synckit::sync::get_sync_key,
41 + // SyncKit — Account & subscription
42 + crate::routes::synckit::sync::sync_account,
43 + crate::routes::synckit::sync::sync_subscription_status,
44 + crate::routes::synckit::sync::get_app_pricing,
45 + crate::routes::synckit::sync::quote_subscription_price,
46 + crate::routes::synckit::sync::create_subscription_checkout,
47 + crate::routes::synckit::sync::queue_storage_cap_change,
48 + // SyncKit — Key rotation
49 + crate::routes::synckit::sync::begin_rotation,
50 + crate::routes::synckit::sync::rotation_entries,
51 + crate::routes::synckit::sync::rotation_batch,
52 + crate::routes::synckit::sync::complete_rotation,
53 + crate::routes::synckit::sync::cancel_rotation,
41 54 // SyncKit — Blobs
42 55 crate::routes::synckit::blobs::blob_upload_url,
43 56 crate::routes::synckit::blobs::blob_confirm_upload,
@@ -76,6 +89,25 @@ use utoipa::OpenApi;
76 89 crate::routes::synckit::BlobConfirmRequest,
77 90 crate::routes::synckit::BlobDownloadUrlRequest,
78 91 crate::routes::synckit::BlobDownloadUrlResponse,
92 + // SyncKit — Account & subscription
93 + crate::routes::synckit::SyncAccountResponse,
94 + crate::routes::synckit::SyncSubscriptionStatusResponse,
95 + crate::routes::synckit::AppPricingRequest,
96 + crate::routes::synckit::AppPricingResponse,
97 + crate::routes::synckit::SyncQuoteRequest,
98 + crate::routes::synckit::SyncQuoteResponse,
99 + crate::routes::synckit::SyncSubscribeRequest,
100 + crate::routes::synckit::SyncCheckoutResponse,
101 + crate::routes::synckit::SyncCapChangeRequest,
102 + // SyncKit — Key rotation
103 + crate::routes::synckit::BeginRotationRequest,
104 + crate::routes::synckit::BeginRotationResponse,
105 + crate::routes::synckit::RotationEntriesRequest,
106 + crate::routes::synckit::RotationEntriesResponse,
107 + crate::routes::synckit::RotationBatchRequest,
108 + crate::routes::synckit::RotationBatchEntry,
109 + crate::routes::synckit::RotationBatchResponse,
110 + crate::routes::synckit::CompleteRotationRequest,
79 111 )),
80 112 tags(
81 113 (name = "License Keys", description = "Public license key validation, activation, and deactivation. Stable API — response shapes are frozen."),
@@ -91,3 +123,32 @@ struct ApiDoc;
91 123 pub async fn openapi_json() -> impl IntoResponse {
92 124 Json(ApiDoc::openapi())
93 125 }
126 +
127 + #[cfg(test)]
128 + mod tests {
129 + use super::*;
130 +
131 + /// The SyncKit rotation + subscription endpoints are fully annotated but were
132 + /// historically missing from the published spec (Run 20). This asserts the
133 + /// spec documents them, so dropping a handler from `paths(...)` regresses
134 + /// here rather than silently shrinking the SDK's contract.
135 + #[test]
136 + fn spec_documents_synckit_rotation_and_subscription() {
137 + let spec = ApiDoc::openapi();
138 + let paths = &spec.paths.paths;
139 + for p in [
140 + "/api/v1/sync/account",
141 + "/api/v1/sync/subscription",
142 + "/api/v1/sync/subscription/checkout",
143 + "/api/v1/sync/subscription/quote",
144 + "/api/v1/sync/subscription/storage-cap",
145 + "/api/v1/sync/app/pricing",
146 + "/api/v1/sync/keys/rotate",
147 + "/api/v1/sync/keys/rotate/batch",
148 + "/api/v1/sync/keys/rotate/complete",
149 + "/api/v1/sync/keys/rotate/entries",
150 + ] {
151 + assert!(paths.contains_key(p), "openapi spec is missing path {p}");
152 + }
153 + }
154 + }