diff --git a/internal/api/openapi.json b/internal/api/openapi.json index dbc3fca..2102075 100644 --- a/internal/api/openapi.json +++ b/internal/api/openapi.json @@ -1 +1 @@ -{"openapi":"3.1.0","info":{"title":"CNAP API","version":"1.0.0","description":"Public API for managing CNAP workspaces, clusters, templates, products, and deployments.\n\nAuthenticate with a Personal Access Token via the `Authorization: Bearer cnap_pat_...` header.\n\nWorkspace-scoped endpoints require the `X-Workspace-Id` header."},"servers":[{"url":"https://api.cnap.tech","description":"Production"}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"Personal Access Token (cnap_pat_...) or OAuth2 JWT. Create tokens at https://dash.cnap.tech/settings/tokens"}},"schemas":{"ApiTokenList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiToken"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"ApiToken":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","example":"My CLI token"},"prefix":{"type":"string","example":"cnap_pat_a3b2"},"created_at":{"type":"number","description":"Unix timestamp (seconds)","example":1708000000},"last_used_at":{"type":"number","nullable":true,"description":"Unix timestamp (seconds) of last use"},"expires_at":{"type":"number","nullable":true,"description":"Unix timestamp (seconds), null if never"}},"required":["id","name","prefix","created_at","last_used_at","expires_at"]},"Pagination":{"type":"object","properties":{"cursor":{"type":"string","nullable":true,"description":"Cursor for next page, null if no more"},"has_more":{"type":"boolean"}},"required":["cursor","has_more"]},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"Resource not found"},"param":{"type":"string","description":"The request field that caused the error","example":"name"},"suggestion":{"type":"string","example":"Run `cnap clusters list` to see available clusters"},"details":{"nullable":true}},"required":["code","message"]}},"required":["error"]},"CreatedToken":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"token":{"type":"string","description":"The full token. This is shown only once — store it securely.","example":"cnap_pat_a3b2c4d5e6f7g8h9i0j1k2l3m4n5o6p7"}},"required":["id","name","token"]},"WorkspaceList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Workspace"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"Workspace":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","example":"My Workspace"},"icon":{"type":"string","nullable":true,"example":null},"created_at":{"type":"number","description":"Unix timestamp (seconds)"}},"required":["id","name","icon","created_at"]},"ClusterList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Cluster"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"Cluster":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","example":"production"},"workspace_id":{"type":"string","example":"j572abc123def456"},"region_id":{"type":"string","example":"j572abc123def456"},"kaas":{"$ref":"#/components/schemas/KaasInfo"},"created_at":{"type":"number","description":"Unix timestamp (seconds)"}},"required":["id","name","workspace_id","region_id","kaas","created_at"]},"KaasInfo":{"type":"object","nullable":true,"properties":{"version":{"type":"string","example":"v1.30"},"status":{"type":"string","enum":["PROVISIONING","RUNNING","RECONCILING","DELETING","ERROR","DEGRADED"],"example":"RUNNING"},"status_message":{"type":"string","nullable":true,"example":null}},"required":["version","status","status_message"],"description":"Present if cluster is KaaS-managed"},"TemplateList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Template"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"Template":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","example":"PostgreSQL HA"},"workspace_id":{"type":"string","example":"j572abc123def456"},"registry_proxy_mode":{"type":"string","nullable":true,"enum":["auto","always","never",null],"example":"auto"},"created_at":{"type":"number","description":"Unix timestamp (seconds)"}},"required":["id","name","workspace_id","registry_proxy_mode","created_at"]},"TemplateDetail":{"allOf":[{"$ref":"#/components/schemas/Template"},{"type":"object","properties":{"helm_sources":{"type":"array","items":{"$ref":"#/components/schemas/HelmSource"}}},"required":["helm_sources"]}]},"HelmSource":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"chart":{"$ref":"#/components/schemas/HelmSourceChart"},"values":{"type":"object","additionalProperties":{"nullable":true}},"metadata":{"type":"object","additionalProperties":{"nullable":true}}},"required":["id","chart"]},"HelmSourceChart":{"type":"object","properties":{"repo_url":{"type":"string","example":"https://charts.bitnami.com/bitnami"},"chart":{"type":"string","example":"postgresql"},"target_revision":{"type":"string","example":"15.5.0"},"path":{"type":"string","example":"charts/my-chart"}},"required":["repo_url","target_revision"]},"ProductList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"Product":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","example":"PostgreSQL Managed"},"workspace_id":{"type":"string","example":"j572abc123def456"},"template_id":{"type":"string","example":"j572abc123def456"},"settings":{"type":"object","nullable":true,"properties":{"custom_image":{"type":"string"},"custom_description":{"type":"string"},"show_sources":{"type":"boolean"}}},"created_at":{"type":"number","description":"Unix timestamp (seconds)"}},"required":["id","name","workspace_id","template_id","settings","created_at"]},"InstallList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Install"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"Install":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","nullable":true,"example":"my-postgres"},"workspace_id":{"type":"string","example":"j572abc123def456"},"product_id":{"type":"string","nullable":true,"description":"Set for product-based installs"},"template_id":{"type":"string","nullable":true,"description":"Template driving this install"},"cluster_id":{"type":"string","example":"j572abc123def456"},"created_at":{"type":"number","description":"Unix timestamp (seconds)"}},"required":["id","name","workspace_id","product_id","template_id","cluster_id","created_at"]},"Pod":{"type":"object","properties":{"name":{"type":"string","example":"postgres-0"},"containers":{"type":"array","items":{"type":"string"},"example":["postgresql"]}},"required":["name","containers"]},"RegionList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Region"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"Region":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","example":"us-east-1"},"icon":{"type":"string","nullable":true,"description":"Icon URL"},"workspace_id":{"type":"string","example":"j572abc123def456"},"created_at":{"type":"number","description":"Unix timestamp (seconds)"}},"required":["id","name","icon","workspace_id","created_at"]},"RegistryCredentialList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RegistryCredential"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"RegistryCredential":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","example":"GitHub Container Registry"},"registry_url":{"type":"string","example":"ghcr.io"},"type":{"type":"string","enum":["basic","token","oauth"],"description":"Auth type"},"is_active":{"type":"boolean","description":"Whether the credential is active"},"created_at":{"type":"number","description":"Unix timestamp (seconds)"},"last_used_at":{"type":"number","nullable":true,"description":"Unix timestamp (seconds)"}},"required":["id","name","registry_url","type","is_active","created_at","last_used_at"]}},"parameters":{}},"paths":{"/v1/user/tokens":{"get":{"tags":["Auth"],"summary":"List personal access tokens","description":"Returns metadata for all tokens. Full token values are never shown after creation.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of tokens","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiTokenList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Auth"],"summary":"Create a personal access token","description":"Creates a new PAT. The full token is returned in the response and never shown again.","security":[{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Human-readable name for the token","example":"My CLI token"},"expires_at":{"type":"integer","minimum":0,"exclusiveMinimum":true,"description":"Unix timestamp (seconds) when the token expires. Omit for no expiry.","example":1742169600}},"required":["name"]}}}},"responses":{"201":{"description":"Token created. The `token` field is shown only once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedToken"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/user/tokens/{id}":{"delete":{"tags":["Auth"],"summary":"Revoke a personal access token","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Token ID"},"required":true,"description":"Token ID","name":"id","in":"path"}],"responses":{"204":{"description":"Token revoked"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Token not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/workspaces":{"get":{"tags":["Workspaces"],"summary":"List workspaces","description":"Lists all workspaces the authenticated user belongs to. No X-Workspace-Id needed.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of workspaces","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/workspaces/{id}":{"get":{"tags":["Workspaces"],"summary":"Get workspace details","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Workspace ID"},"required":true,"description":"Workspace ID","name":"id","in":"path"}],"responses":{"200":{"description":"Workspace details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Workspace"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not a member of this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Workspace not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/clusters":{"get":{"tags":["Clusters"],"summary":"List clusters in workspace","description":"Lists all clusters in the workspace specified by the X-Workspace-Id header.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of clusters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/clusters/{id}":{"get":{"tags":["Clusters"],"summary":"Get cluster details","description":"Returns detailed information about a cluster, including KaaS status if applicable.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Cluster ID"},"required":true,"description":"Cluster ID","name":"id","in":"path"}],"responses":{"200":{"description":"Cluster details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Cluster"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not a member of the cluster workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Cluster not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Clusters"],"summary":"Update cluster","description":"Update cluster name or region. The region must be in the same workspace.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Cluster ID"},"required":true,"description":"Cluster ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"example":"staging"},"region_id":{"type":"string","description":"Region ID"}}}}}},"responses":{"200":{"description":"Updated cluster","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Cluster"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not a member of the cluster workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Cluster not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Clusters"],"summary":"Delete cluster","description":"Deletes a cluster and its configuration. KaaS-managed clusters are deprovisioned. Fails if the cluster has active installations.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Cluster ID"},"required":true,"description":"Cluster ID","name":"id","in":"path"}],"responses":{"204":{"description":"Cluster deleted"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not a member of the cluster workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Cluster not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Cluster has active installations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/clusters/{id}/kubeconfig":{"get":{"tags":["Clusters"],"summary":"Get cluster kubeconfig","description":"Returns the admin kubeconfig for a KaaS-managed cluster. The cluster must be in RUNNING status.\n\nSupports content negotiation via the `Accept` header:\n- `application/json` — returns JSON (default for API clients)\n- `application/yaml` — returns YAML (default for kubectl/CLI)","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Cluster ID"},"required":true,"description":"Cluster ID","name":"id","in":"path"}],"responses":{"200":{"description":"Admin kubeconfig","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"nullable":true},"description":"Kubeconfig as JSON"}},"application/yaml":{"schema":{"type":"string"}}}},"400":{"description":"Cluster is not in RUNNING status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not a member of the cluster workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Cluster not found or not KaaS-managed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/templates":{"get":{"tags":["Templates"],"summary":"List templates in workspace","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of templates","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Templates"],"summary":"Create template","security":[{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"example":"PostgreSQL HA"},"sources":{"type":"array","items":{"type":"object","properties":{"chart":{"type":"object","properties":{"repo_url":{"type":"string","minLength":1,"maxLength":2048},"chart":{"type":"string","minLength":1,"maxLength":100},"target_revision":{"type":"string","minLength":1,"maxLength":100},"path":{"type":"string","minLength":1,"maxLength":500}},"required":["repo_url","target_revision"]},"values":{"type":"object","additionalProperties":{"nullable":true}},"metadata":{"type":"object","properties":{"artifact_hub_helm_package":{"type":"object","additionalProperties":{"nullable":true}},"image":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2048},"tag":{"type":"string","minLength":1,"maxLength":200},"github":{"type":"object","properties":{"workflow_run_id":{"type":"string","minLength":1,"maxLength":100},"repository":{"type":"object","properties":{"id":{"type":"number"},"html_url":{"type":"string","minLength":1,"maxLength":2048},"name":{"type":"string","minLength":1,"maxLength":100},"full_name":{"type":"string","minLength":1,"maxLength":200},"owner":{"type":"object","properties":{"login":{"type":"string","minLength":1,"maxLength":100},"id":{"type":"number"},"type":{"type":"string","minLength":1,"maxLength":50},"avatar_url":{"type":"string","minLength":1,"maxLength":2048},"html_url":{"type":"string","minLength":1,"maxLength":2048}},"required":["login","id","type","avatar_url","html_url"]}},"required":["id","html_url","name","full_name","owner"]}}}},"required":["url","tag"]},"auto_deploy":{"type":"boolean"}}}},"required":["chart"]},"minItems":1},"registry_proxy_mode":{"type":"string","enum":["auto","always","never"]}},"required":["name","sources"]}}}},"responses":{"201":{"description":"Template created","content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"string"}},"required":["template_id"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/templates/{id}":{"get":{"tags":["Templates"],"summary":"Get template details","description":"Returns template with its helm sources.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Template ID"},"required":true,"description":"Template ID","name":"id","in":"path"}],"responses":{"200":{"description":"Template details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateDetail"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Template not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Templates"],"summary":"Update template","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Template ID"},"required":true,"description":"Template ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"sources":{"type":"array","items":{"type":"object","properties":{"chart":{"type":"object","properties":{"repo_url":{"type":"string","minLength":1,"maxLength":2048},"chart":{"type":"string","minLength":1,"maxLength":100},"target_revision":{"type":"string","minLength":1,"maxLength":100},"path":{"type":"string","minLength":1,"maxLength":500}},"required":["repo_url","target_revision"]},"values":{"type":"object","additionalProperties":{"nullable":true}},"metadata":{"type":"object","properties":{"artifact_hub_helm_package":{"type":"object","additionalProperties":{"nullable":true}},"image":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2048},"tag":{"type":"string","minLength":1,"maxLength":200},"github":{"type":"object","properties":{"workflow_run_id":{"type":"string","minLength":1,"maxLength":100},"repository":{"type":"object","properties":{"id":{"type":"number"},"html_url":{"type":"string","minLength":1,"maxLength":2048},"name":{"type":"string","minLength":1,"maxLength":100},"full_name":{"type":"string","minLength":1,"maxLength":200},"owner":{"type":"object","properties":{"login":{"type":"string","minLength":1,"maxLength":100},"id":{"type":"number"},"type":{"type":"string","minLength":1,"maxLength":50},"avatar_url":{"type":"string","minLength":1,"maxLength":2048},"html_url":{"type":"string","minLength":1,"maxLength":2048}},"required":["login","id","type","avatar_url","html_url"]}},"required":["id","html_url","name","full_name","owner"]}}}},"required":["url","tag"]},"auto_deploy":{"type":"boolean"}}}},"required":["chart"]},"minItems":1},"registry_proxy_mode":{"type":"string","enum":["auto","always","never"]}}}}}},"responses":{"200":{"description":"Template updated","content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"string"}},"required":["template_id"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Template not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Templates"],"summary":"Delete template","description":"Fails if referenced by products or installs.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Template ID"},"required":true,"description":"Template ID","name":"id","in":"path"}],"responses":{"204":{"description":"Template deleted"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Template not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Template is referenced by products or installs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/products":{"get":{"tags":["Products"],"summary":"List products in workspace","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of products","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Products"],"summary":"Create product","description":"Creates a product with helm sources and cluster associations. Triggers async chart generation.","security":[{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":32,"example":"PostgreSQL Managed"},"sources":{"type":"array","items":{"type":"object","properties":{"chart":{"type":"object","properties":{"repo_url":{"type":"string","minLength":1,"maxLength":2048},"chart":{"type":"string","minLength":1,"maxLength":100},"target_revision":{"type":"string","minLength":1,"maxLength":100},"path":{"type":"string","minLength":1,"maxLength":500}},"required":["repo_url","target_revision"]},"values":{"type":"object","additionalProperties":{"nullable":true}},"metadata":{"type":"object","additionalProperties":{"nullable":true}}},"required":["chart"]},"minItems":1},"cluster_ids":{"type":"array","items":{"type":"string"},"minItems":1,"description":"Cluster IDs to deploy to"},"stripe_price_ids":{"type":"array","items":{"type":"string"}},"settings":{"type":"object","properties":{"custom_image":{"type":"string","minLength":1,"maxLength":2048},"custom_description":{"type":"string","minLength":1,"maxLength":500},"show_sources":{"type":"boolean"}}}},"required":["name","sources","cluster_ids"]}}}},"responses":{"201":{"description":"Product created","content":{"application/json":{"schema":{"type":"object","properties":{"product_id":{"type":"string"},"template_id":{"type":"string"}},"required":["product_id","template_id"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/products/{id}":{"get":{"tags":["Products"],"summary":"Get product details","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID"},"required":true,"description":"Product ID","name":"id","in":"path"}],"responses":{"200":{"description":"Product details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Product not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Products"],"summary":"Update product","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID"},"required":true,"description":"Product ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":32,"example":"PostgreSQL Managed"},"sources":{"type":"array","items":{"type":"object","properties":{"chart":{"type":"object","properties":{"repo_url":{"type":"string","minLength":1,"maxLength":2048},"chart":{"type":"string","minLength":1,"maxLength":100},"target_revision":{"type":"string","minLength":1,"maxLength":100},"path":{"type":"string","minLength":1,"maxLength":500}},"required":["repo_url","target_revision"]},"values":{"type":"object","additionalProperties":{"nullable":true}},"metadata":{"type":"object","additionalProperties":{"nullable":true}}},"required":["chart"]},"minItems":1},"cluster_ids":{"type":"array","items":{"type":"string"},"minItems":1},"stripe_price_ids":{"type":"array","items":{"type":"string"}},"settings":{"type":"object","properties":{"custom_image":{"type":"string","minLength":1,"maxLength":2048},"custom_description":{"type":"string","minLength":1,"maxLength":500},"show_sources":{"type":"boolean"}}}},"required":["name","sources","cluster_ids"]}}}},"responses":{"200":{"description":"Product updated","content":{"application/json":{"schema":{"type":"object","properties":{"product_id":{"type":"string"},"template_id":{"type":"string"}},"required":["product_id","template_id"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Product not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Products"],"summary":"Delete product","description":"Fails if the product has active installs.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID"},"required":true,"description":"Product ID","name":"id","in":"path"}],"responses":{"204":{"description":"Product deleted"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Product not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Product has active installs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/installs":{"get":{"tags":["Installs"],"summary":"List installs in workspace","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of installs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Installs"],"summary":"Create product install","description":"Deploys a product to a region. Pass an `Idempotency-Key` header to safely retry on timeout.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256,"description":"Unique key to prevent duplicate operations. If the same key is sent again, the existing workflow is returned instead of creating a new one.","example":"deploy-prod-2024-03-15"},"required":false,"description":"Unique key to prevent duplicate operations. If the same key is sent again, the existing workflow is returned instead of creating a new one.","name":"idempotency-key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"product_id":{"type":"string","minLength":1,"description":"Product ID"},"region_id":{"type":"string","minLength":1,"description":"Region ID"},"overrides":{"type":"array","items":{"type":"object","properties":{"template_helm_source_id":{"type":"string","minLength":1,"description":"Helm source ID to override"},"values":{"type":"object","additionalProperties":{"nullable":true},"description":"Helm values"}},"required":["template_helm_source_id","values"]},"description":"Initial value overrides per helm source"}},"required":["product_id","region_id"]}}}},"responses":{"202":{"description":"Install workflow started"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/installs/{id}":{"get":{"tags":["Installs"],"summary":"Get install details","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Install ID"},"required":true,"description":"Install ID","name":"id","in":"path"}],"responses":{"200":{"description":"Install details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Install"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Install not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Installs"],"summary":"Delete install","description":"Triggers async deletion of the install and its resources.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Install ID"},"required":true,"description":"Install ID","name":"id","in":"path"}],"responses":{"202":{"description":"Deletion started"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Install not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/installs/{id}/pods":{"get":{"tags":["Installs"],"summary":"List pods for install","description":"Returns pods and their containers for the install.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Install ID"},"required":true,"description":"Install ID","name":"id","in":"path"}],"responses":{"200":{"description":"List of pods","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Pod"}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Install not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/installs/standalone":{"post":{"tags":["Installs"],"summary":"Create standalone install","description":"Deploys custom helm sources directly to clusters without a product. Pass an `Idempotency-Key` header to safely retry on timeout.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256,"description":"Unique key to prevent duplicate operations. If the same key is sent again, the existing workflow is returned instead of creating a new one.","example":"deploy-prod-2024-03-15"},"required":false,"description":"Unique key to prevent duplicate operations. If the same key is sent again, the existing workflow is returned instead of creating a new one.","name":"idempotency-key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"example":"my-redis"},"cluster_ids":{"type":"array","items":{"type":"string"},"minItems":1,"description":"Cluster IDs to deploy to"},"helm_sources":{"type":"array","items":{"type":"object","properties":{"chart":{"type":"object","properties":{"repo_url":{"type":"string","minLength":1,"maxLength":2048},"chart":{"type":"string","minLength":1,"maxLength":100},"target_revision":{"type":"string","minLength":1,"maxLength":100},"path":{"type":"string","minLength":1,"maxLength":500}},"required":["repo_url","target_revision"]},"values":{"type":"object","additionalProperties":{"nullable":true}},"metadata":{"type":"object","additionalProperties":{"nullable":true}}},"required":["chart"]},"minItems":1}},"required":["name","cluster_ids","helm_sources"]}}}},"responses":{"202":{"description":"Install workflow started"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/installs/{id}/values":{"patch":{"tags":["Installs"],"summary":"Update install template values","description":"Updates template helm source values and regenerates the chart. Use this for standalone installs or to change the base values of a product install.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Install ID"},"required":true,"description":"Install ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"updates":{"type":"array","items":{"type":"object","properties":{"template_helm_source_id":{"type":"string","minLength":1,"description":"Helm source ID"},"values":{"type":"object","additionalProperties":{"nullable":true},"description":"Helm values"}},"required":["template_helm_source_id","values"]},"minItems":1}},"required":["updates"]}}}},"responses":{"202":{"description":"Update workflow started"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Install not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/installs/{id}/overrides":{"patch":{"tags":["Installs"],"summary":"Update install value overrides","description":"Applies per-install value overrides on top of the product base values. Does not regenerate the chart.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Install ID"},"required":true,"description":"Install ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"updates":{"type":"array","items":{"type":"object","properties":{"template_helm_source_id":{"type":"string","minLength":1,"description":"Helm source ID"},"values":{"type":"object","additionalProperties":{"nullable":true},"description":"Override values"}},"required":["template_helm_source_id","values"]},"minItems":1}},"required":["updates"]}}}},"responses":{"202":{"description":"Update workflow started"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Install not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/installs/{id}/logs":{"get":{"tags":["Installs"],"summary":"Stream install logs","description":"Streams logs from the install pods via Server-Sent Events. Use the `Accept: text/event-stream` header.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Install ID"},"required":true,"description":"Install ID","name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":253,"description":"Pod name (all pods if omitted)"},"required":false,"description":"Pod name (all pods if omitted)","name":"pod","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":253,"description":"Container name"},"required":false,"description":"Container name","name":"container","in":"query"},{"schema":{"type":"boolean","nullable":true,"default":true,"description":"Follow log output"},"required":false,"description":"Follow log output","name":"follow","in":"query"},{"schema":{"type":"integer","nullable":true,"description":"Lines to tail"},"required":false,"description":"Lines to tail","name":"tail","in":"query"},{"schema":{"type":"integer","nullable":true,"default":0,"description":"Only return logs newer than this many seconds"},"required":false,"description":"Only return logs newer than this many seconds","name":"since_seconds","in":"query"}],"responses":{"200":{"description":"Log stream (text/event-stream)"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Install not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/regions":{"get":{"tags":["Regions"],"summary":"List regions in workspace","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of regions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegionList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Regions"],"summary":"Create region","security":[{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"example":"us-east-1"},"icon":{"type":"string","maxLength":256,"format":"uri","description":"Icon URL"}},"required":["name"]}}}},"responses":{"201":{"description":"Region created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Region"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/registry/credentials":{"get":{"tags":["Registry"],"summary":"List registry credentials","description":"Returns credentials for the workspace. Credential secrets are never exposed.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegistryCredentialList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Registry"],"summary":"Create registry credential","description":"Adds a registry credential to the workspace for OCI proxy authentication.","security":[{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"example":"GitHub Container Registry"},"registry_url":{"type":"string","minLength":1,"maxLength":2048,"example":"ghcr.io"},"type":{"type":"string","enum":["basic","token","oauth"],"description":"Auth type"},"credentials":{"type":"object","properties":{"username":{"type":"string","minLength":1,"maxLength":200},"password":{"type":"string","minLength":1,"maxLength":500},"token":{"type":"string","minLength":1,"maxLength":2000},"client_id":{"type":"string","minLength":1,"maxLength":200},"client_secret":{"type":"string","minLength":1,"maxLength":500},"token_url":{"type":"string","minLength":1,"maxLength":2048}},"description":"Auth credentials (type-dependent)"}},"required":["name","registry_url","type","credentials"]}}}},"responses":{"201":{"description":"Credential created"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/registry/credentials/{id}":{"delete":{"tags":["Registry"],"summary":"Delete registry credential","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Credential ID"},"required":true,"description":"Credential ID","name":"id","in":"path"}],"responses":{"204":{"description":"Credential deleted"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Credential not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}} \ No newline at end of file +{"openapi":"3.1.0","info":{"title":"CNAP API","version":"1.0.0","description":"Public API for managing CNAP workspaces, clusters, templates, products, and deployments.\n\nAuthenticate with a Personal Access Token via the `Authorization: Bearer cnap_pat_...` header.\n\nWorkspace-scoped endpoints require the `X-Workspace-Id` header."},"servers":[{"url":"https://api.cnap.tech","description":"Production"}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"Personal Access Token (cnap_pat_...) or OAuth2 JWT. Create tokens at https://cnap.tech/settings/tokens"}},"schemas":{"ApiTokenList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiToken"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"ApiToken":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","example":"My CLI token"},"prefix":{"type":"string","example":"cnap_pat_a3b2"},"created_at":{"type":"number","description":"Unix timestamp (seconds)","example":1708000000},"last_used_at":{"type":"number","nullable":true,"description":"Unix timestamp (seconds) of last use"},"expires_at":{"type":"number","nullable":true,"description":"Unix timestamp (seconds), null if never"}},"required":["id","name","prefix","created_at","last_used_at","expires_at"]},"Pagination":{"type":"object","properties":{"cursor":{"type":"string","nullable":true,"description":"Cursor for next page, null if no more"},"has_more":{"type":"boolean"}},"required":["cursor","has_more"]},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"Resource not found"},"param":{"type":"string","description":"The request field that caused the error","example":"name"},"suggestion":{"type":"string","example":"Run `cnap clusters list` to see available clusters"},"details":{"nullable":true}},"required":["code","message"]}},"required":["error"]},"CreatedToken":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"token":{"type":"string","description":"The full token. This is shown only once — store it securely.","example":"cnap_pat_a3b2c4d5e6f7g8h9i0j1k2l3m4n5o6p7"}},"required":["id","name","token"]},"WorkspaceList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Workspace"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"Workspace":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","example":"My Workspace"},"icon":{"type":"string","nullable":true,"example":null},"created_at":{"type":"number","description":"Unix timestamp (seconds)"}},"required":["id","name","icon","created_at"]},"ClusterList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Cluster"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"Cluster":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","example":"production"},"workspace_id":{"type":"string","example":"j572abc123def456"},"region_id":{"type":"string","example":"j572abc123def456"},"kaas":{"$ref":"#/components/schemas/KaasInfo"},"created_at":{"type":"number","description":"Unix timestamp (seconds)"}},"required":["id","name","workspace_id","region_id","kaas","created_at"]},"KaasInfo":{"type":"object","nullable":true,"properties":{"version":{"type":"string","example":"v1.30"},"status":{"type":"string","enum":["PROVISIONING","RUNNING","RECONCILING","DELETING","ERROR","DEGRADED"],"example":"RUNNING"},"status_message":{"type":"string","nullable":true,"example":null}},"required":["version","status","status_message"],"description":"Present if cluster is KaaS-managed"},"TemplateList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Template"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"Template":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","example":"PostgreSQL HA"},"workspace_id":{"type":"string","example":"j572abc123def456"},"registry_proxy_mode":{"type":"string","nullable":true,"enum":["auto","always","never",null],"example":"auto"},"created_at":{"type":"number","description":"Unix timestamp (seconds)"}},"required":["id","name","workspace_id","registry_proxy_mode","created_at"]},"TemplateDetail":{"allOf":[{"$ref":"#/components/schemas/Template"},{"type":"object","properties":{"helm_sources":{"type":"array","items":{"$ref":"#/components/schemas/HelmSource"}}},"required":["helm_sources"]}]},"HelmSource":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"chart":{"$ref":"#/components/schemas/HelmSourceChart"},"values":{"type":"object","additionalProperties":{"nullable":true}},"metadata":{"type":"object","additionalProperties":{"nullable":true}}},"required":["id","chart"]},"HelmSourceChart":{"type":"object","properties":{"repo_url":{"type":"string","example":"https://charts.bitnami.com/bitnami"},"chart":{"type":"string","example":"postgresql"},"target_revision":{"type":"string","example":"15.5.0"},"path":{"type":"string","example":"charts/my-chart"}},"required":["repo_url","target_revision"]},"ProductList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"Product":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","example":"PostgreSQL Managed"},"workspace_id":{"type":"string","example":"j572abc123def456"},"template_id":{"type":"string","example":"j572abc123def456"},"settings":{"type":"object","nullable":true,"properties":{"custom_image":{"type":"string"},"custom_description":{"type":"string"},"show_sources":{"type":"boolean"}}},"created_at":{"type":"number","description":"Unix timestamp (seconds)"}},"required":["id","name","workspace_id","template_id","settings","created_at"]},"InstallList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Install"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"Install":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","nullable":true,"example":"my-postgres"},"workspace_id":{"type":"string","example":"j572abc123def456"},"product_id":{"type":"string","nullable":true,"description":"Set for product-based installs"},"template_id":{"type":"string","nullable":true,"description":"Template driving this install"},"cluster_id":{"type":"string","example":"j572abc123def456"},"created_at":{"type":"number","description":"Unix timestamp (seconds)"}},"required":["id","name","workspace_id","product_id","template_id","cluster_id","created_at"]},"Pod":{"type":"object","properties":{"name":{"type":"string","example":"postgres-0"},"containers":{"type":"array","items":{"type":"string"},"example":["postgresql"]}},"required":["name","containers"]},"RegionList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Region"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"Region":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","example":"us-east-1"},"icon":{"type":"string","nullable":true,"description":"Icon URL"},"workspace_id":{"type":"string","example":"j572abc123def456"},"created_at":{"type":"number","description":"Unix timestamp (seconds)"}},"required":["id","name","icon","workspace_id","created_at"]},"RegistryCredentialList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RegistryCredential"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"RegistryCredential":{"type":"object","properties":{"id":{"type":"string","example":"j572abc123def456"},"name":{"type":"string","example":"GitHub Container Registry"},"registry_url":{"type":"string","example":"ghcr.io"},"type":{"type":"string","enum":["basic","token","oauth"],"description":"Auth type"},"is_active":{"type":"boolean","description":"Whether the credential is active"},"created_at":{"type":"number","description":"Unix timestamp (seconds)"},"last_used_at":{"type":"number","nullable":true,"description":"Unix timestamp (seconds)"}},"required":["id","name","registry_url","type","is_active","created_at","last_used_at"]}},"parameters":{}},"paths":{"/v1/user/tokens":{"get":{"tags":["Auth"],"summary":"List personal access tokens","description":"Returns metadata for all tokens. Full token values are never shown after creation.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of tokens","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiTokenList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Auth"],"summary":"Create a personal access token","description":"Creates a new PAT. The full token is returned in the response and never shown again.","security":[{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Human-readable name for the token","example":"My CLI token"},"expires_at":{"type":"integer","minimum":0,"exclusiveMinimum":true,"description":"Unix timestamp (seconds) when the token expires. Omit for no expiry.","example":1742169600}},"required":["name"]}}}},"responses":{"201":{"description":"Token created. The `token` field is shown only once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedToken"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/user/tokens/{id}":{"delete":{"tags":["Auth"],"summary":"Revoke a personal access token","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Token ID"},"required":true,"description":"Token ID","name":"id","in":"path"}],"responses":{"204":{"description":"Token revoked"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Token not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/workspaces":{"get":{"tags":["Workspaces"],"summary":"List workspaces","description":"Lists all workspaces the authenticated user belongs to. No X-Workspace-Id needed.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of workspaces","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/workspaces/{id}":{"get":{"tags":["Workspaces"],"summary":"Get workspace details","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Workspace ID"},"required":true,"description":"Workspace ID","name":"id","in":"path"}],"responses":{"200":{"description":"Workspace details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Workspace"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not a member of this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Workspace not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/clusters":{"get":{"tags":["Clusters"],"summary":"List clusters in workspace","description":"Lists all clusters in the workspace specified by the X-Workspace-Id header.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of clusters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/clusters/{id}":{"get":{"tags":["Clusters"],"summary":"Get cluster details","description":"Returns detailed information about a cluster, including KaaS status if applicable.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Cluster ID"},"required":true,"description":"Cluster ID","name":"id","in":"path"}],"responses":{"200":{"description":"Cluster details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Cluster"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not a member of the cluster workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Cluster not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Clusters"],"summary":"Update cluster","description":"Update cluster name or region. The region must be in the same workspace.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Cluster ID"},"required":true,"description":"Cluster ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"example":"staging"},"region_id":{"type":"string","description":"Region ID"}}}}}},"responses":{"200":{"description":"Updated cluster","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Cluster"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not a member of the cluster workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Cluster not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Clusters"],"summary":"Delete cluster","description":"Deletes a cluster and its configuration. KaaS-managed clusters are deprovisioned. Fails if the cluster has active installations.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Cluster ID"},"required":true,"description":"Cluster ID","name":"id","in":"path"}],"responses":{"204":{"description":"Cluster deleted"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not a member of the cluster workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Cluster not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Cluster has active installations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/clusters/{id}/kubeconfig":{"get":{"tags":["Clusters"],"summary":"Get cluster kubeconfig","description":"Returns the admin kubeconfig for a KaaS-managed cluster. The cluster must be in RUNNING status.\n\nSupports content negotiation via the `Accept` header:\n- `application/json` — returns JSON (default for API clients)\n- `application/yaml` — returns YAML (default for kubectl/CLI)","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Cluster ID"},"required":true,"description":"Cluster ID","name":"id","in":"path"}],"responses":{"200":{"description":"Admin kubeconfig","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"nullable":true},"description":"Kubeconfig as JSON"}},"application/yaml":{"schema":{"type":"string"}}}},"400":{"description":"Cluster is not in RUNNING status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not a member of the cluster workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Cluster not found or not KaaS-managed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/templates":{"get":{"tags":["Templates"],"summary":"List templates in workspace","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of templates","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Templates"],"summary":"Create template","security":[{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"example":"PostgreSQL HA"},"sources":{"type":"array","items":{"type":"object","properties":{"chart":{"type":"object","properties":{"repo_url":{"type":"string","minLength":1,"maxLength":2048},"chart":{"type":"string","minLength":1,"maxLength":100},"target_revision":{"type":"string","minLength":1,"maxLength":100},"path":{"type":"string","minLength":1,"maxLength":500}},"required":["repo_url","target_revision"]},"values":{"type":"object","additionalProperties":{"nullable":true}},"metadata":{"type":"object","properties":{"artifact_hub_helm_package":{"type":"object","additionalProperties":{"nullable":true}},"image":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2048},"tag":{"type":"string","minLength":1,"maxLength":200},"github":{"type":"object","properties":{"workflow_run_id":{"type":"string","minLength":1,"maxLength":100},"repository":{"type":"object","properties":{"id":{"type":"number"},"html_url":{"type":"string","minLength":1,"maxLength":2048},"name":{"type":"string","minLength":1,"maxLength":100},"full_name":{"type":"string","minLength":1,"maxLength":200},"owner":{"type":"object","properties":{"login":{"type":"string","minLength":1,"maxLength":100},"id":{"type":"number"},"type":{"type":"string","minLength":1,"maxLength":50},"avatar_url":{"type":"string","minLength":1,"maxLength":2048},"html_url":{"type":"string","minLength":1,"maxLength":2048}},"required":["login","id","type","avatar_url","html_url"]}},"required":["id","html_url","name","full_name","owner"]}}}},"required":["url","tag"]},"auto_deploy":{"type":"boolean"}}}},"required":["chart"]},"minItems":1},"registry_proxy_mode":{"type":"string","enum":["auto","always","never"]}},"required":["name","sources"]}}}},"responses":{"201":{"description":"Template created","content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"string"}},"required":["template_id"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/templates/{id}":{"get":{"tags":["Templates"],"summary":"Get template details","description":"Returns template with its helm sources.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Template ID"},"required":true,"description":"Template ID","name":"id","in":"path"}],"responses":{"200":{"description":"Template details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateDetail"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Template not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Templates"],"summary":"Update template","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Template ID"},"required":true,"description":"Template ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"sources":{"type":"array","items":{"type":"object","properties":{"chart":{"type":"object","properties":{"repo_url":{"type":"string","minLength":1,"maxLength":2048},"chart":{"type":"string","minLength":1,"maxLength":100},"target_revision":{"type":"string","minLength":1,"maxLength":100},"path":{"type":"string","minLength":1,"maxLength":500}},"required":["repo_url","target_revision"]},"values":{"type":"object","additionalProperties":{"nullable":true}},"metadata":{"type":"object","properties":{"artifact_hub_helm_package":{"type":"object","additionalProperties":{"nullable":true}},"image":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2048},"tag":{"type":"string","minLength":1,"maxLength":200},"github":{"type":"object","properties":{"workflow_run_id":{"type":"string","minLength":1,"maxLength":100},"repository":{"type":"object","properties":{"id":{"type":"number"},"html_url":{"type":"string","minLength":1,"maxLength":2048},"name":{"type":"string","minLength":1,"maxLength":100},"full_name":{"type":"string","minLength":1,"maxLength":200},"owner":{"type":"object","properties":{"login":{"type":"string","minLength":1,"maxLength":100},"id":{"type":"number"},"type":{"type":"string","minLength":1,"maxLength":50},"avatar_url":{"type":"string","minLength":1,"maxLength":2048},"html_url":{"type":"string","minLength":1,"maxLength":2048}},"required":["login","id","type","avatar_url","html_url"]}},"required":["id","html_url","name","full_name","owner"]}}}},"required":["url","tag"]},"auto_deploy":{"type":"boolean"}}}},"required":["chart"]},"minItems":1},"registry_proxy_mode":{"type":"string","enum":["auto","always","never"]}}}}}},"responses":{"200":{"description":"Template updated","content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"string"}},"required":["template_id"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Template not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Templates"],"summary":"Delete template","description":"Fails if referenced by products or installs.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Template ID"},"required":true,"description":"Template ID","name":"id","in":"path"}],"responses":{"204":{"description":"Template deleted"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Template not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Template is referenced by products or installs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/products":{"get":{"tags":["Products"],"summary":"List products in workspace","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of products","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Products"],"summary":"Create product","description":"Creates a product with helm sources and cluster associations. Triggers async chart generation.","security":[{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":32,"example":"PostgreSQL Managed"},"sources":{"type":"array","items":{"type":"object","properties":{"chart":{"type":"object","properties":{"repo_url":{"type":"string","minLength":1,"maxLength":2048},"chart":{"type":"string","minLength":1,"maxLength":100},"target_revision":{"type":"string","minLength":1,"maxLength":100},"path":{"type":"string","minLength":1,"maxLength":500}},"required":["repo_url","target_revision"]},"values":{"type":"object","additionalProperties":{"nullable":true}},"metadata":{"type":"object","additionalProperties":{"nullable":true}}},"required":["chart"]},"minItems":1},"cluster_ids":{"type":"array","items":{"type":"string"},"minItems":1,"description":"Cluster IDs to deploy to"},"stripe_price_ids":{"type":"array","items":{"type":"string"}},"settings":{"type":"object","properties":{"custom_image":{"type":"string","minLength":1,"maxLength":2048},"custom_description":{"type":"string","minLength":1,"maxLength":500},"show_sources":{"type":"boolean"}}}},"required":["name","sources","cluster_ids"]}}}},"responses":{"201":{"description":"Product created","content":{"application/json":{"schema":{"type":"object","properties":{"product_id":{"type":"string"},"template_id":{"type":"string"}},"required":["product_id","template_id"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/products/{id}":{"get":{"tags":["Products"],"summary":"Get product details","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID"},"required":true,"description":"Product ID","name":"id","in":"path"}],"responses":{"200":{"description":"Product details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Product not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Products"],"summary":"Update product","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID"},"required":true,"description":"Product ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":32,"example":"PostgreSQL Managed"},"sources":{"type":"array","items":{"type":"object","properties":{"chart":{"type":"object","properties":{"repo_url":{"type":"string","minLength":1,"maxLength":2048},"chart":{"type":"string","minLength":1,"maxLength":100},"target_revision":{"type":"string","minLength":1,"maxLength":100},"path":{"type":"string","minLength":1,"maxLength":500}},"required":["repo_url","target_revision"]},"values":{"type":"object","additionalProperties":{"nullable":true}},"metadata":{"type":"object","additionalProperties":{"nullable":true}}},"required":["chart"]},"minItems":1},"cluster_ids":{"type":"array","items":{"type":"string"},"minItems":1},"stripe_price_ids":{"type":"array","items":{"type":"string"}},"settings":{"type":"object","properties":{"custom_image":{"type":"string","minLength":1,"maxLength":2048},"custom_description":{"type":"string","minLength":1,"maxLength":500},"show_sources":{"type":"boolean"}}}},"required":["name","sources","cluster_ids"]}}}},"responses":{"200":{"description":"Product updated","content":{"application/json":{"schema":{"type":"object","properties":{"product_id":{"type":"string"},"template_id":{"type":"string"}},"required":["product_id","template_id"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Product not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Products"],"summary":"Delete product","description":"Fails if the product has active installs.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Product ID"},"required":true,"description":"Product ID","name":"id","in":"path"}],"responses":{"204":{"description":"Product deleted"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Product not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Product has active installs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/installs":{"get":{"tags":["Installs"],"summary":"List installs in workspace","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of installs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Installs"],"summary":"Create product install","description":"Deploys a product to a region. Pass an `Idempotency-Key` header to safely retry on timeout.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256,"description":"Unique key to prevent duplicate operations. If the same key is sent again, the existing workflow is returned instead of creating a new one.","example":"deploy-prod-2024-03-15"},"required":false,"description":"Unique key to prevent duplicate operations. If the same key is sent again, the existing workflow is returned instead of creating a new one.","name":"idempotency-key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"product_id":{"type":"string","minLength":1,"description":"Product ID"},"region_id":{"type":"string","minLength":1,"description":"Region ID"},"overrides":{"type":"array","items":{"type":"object","properties":{"template_helm_source_id":{"type":"string","minLength":1,"description":"Helm source ID to override"},"values":{"type":"object","additionalProperties":{"nullable":true},"description":"Helm values"}},"required":["template_helm_source_id","values"]},"description":"Initial value overrides per helm source"}},"required":["product_id","region_id"]}}}},"responses":{"202":{"description":"Install workflow started"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/installs/{id}":{"get":{"tags":["Installs"],"summary":"Get install details","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Install ID"},"required":true,"description":"Install ID","name":"id","in":"path"}],"responses":{"200":{"description":"Install details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Install"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Install not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Installs"],"summary":"Delete install","description":"Triggers async deletion of the install and its resources.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Install ID"},"required":true,"description":"Install ID","name":"id","in":"path"}],"responses":{"202":{"description":"Deletion started"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Install not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/installs/{id}/pods":{"get":{"tags":["Installs"],"summary":"List pods for install","description":"Returns pods and their containers for the install.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Install ID"},"required":true,"description":"Install ID","name":"id","in":"path"}],"responses":{"200":{"description":"List of pods","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Pod"}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Install not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/installs/standalone":{"post":{"tags":["Installs"],"summary":"Create standalone install","description":"Deploys custom helm sources directly to clusters without a product. Pass an `Idempotency-Key` header to safely retry on timeout.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256,"description":"Unique key to prevent duplicate operations. If the same key is sent again, the existing workflow is returned instead of creating a new one.","example":"deploy-prod-2024-03-15"},"required":false,"description":"Unique key to prevent duplicate operations. If the same key is sent again, the existing workflow is returned instead of creating a new one.","name":"idempotency-key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"example":"my-redis"},"cluster_ids":{"type":"array","items":{"type":"string"},"minItems":1,"description":"Cluster IDs to deploy to"},"helm_sources":{"type":"array","items":{"type":"object","properties":{"chart":{"type":"object","properties":{"repo_url":{"type":"string","minLength":1,"maxLength":2048},"chart":{"type":"string","minLength":1,"maxLength":100},"target_revision":{"type":"string","minLength":1,"maxLength":100},"path":{"type":"string","minLength":1,"maxLength":500}},"required":["repo_url","target_revision"]},"values":{"type":"object","additionalProperties":{"nullable":true}},"metadata":{"type":"object","additionalProperties":{"nullable":true}}},"required":["chart"]},"minItems":1}},"required":["name","cluster_ids","helm_sources"]}}}},"responses":{"202":{"description":"Install workflow started"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/installs/{id}/values":{"patch":{"tags":["Installs"],"summary":"Update install template values","description":"Updates template helm source values and regenerates the chart. Use this for standalone installs or to change the base values of a product install.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Install ID"},"required":true,"description":"Install ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"updates":{"type":"array","items":{"type":"object","properties":{"template_helm_source_id":{"type":"string","minLength":1,"description":"Helm source ID"},"values":{"type":"object","additionalProperties":{"nullable":true},"description":"Helm values"}},"required":["template_helm_source_id","values"]},"minItems":1}},"required":["updates"]}}}},"responses":{"202":{"description":"Update workflow started"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Install not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/installs/{id}/overrides":{"patch":{"tags":["Installs"],"summary":"Update install value overrides","description":"Applies per-install value overrides on top of the product base values. Does not regenerate the chart.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Install ID"},"required":true,"description":"Install ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"updates":{"type":"array","items":{"type":"object","properties":{"template_helm_source_id":{"type":"string","minLength":1,"description":"Helm source ID"},"values":{"type":"object","additionalProperties":{"nullable":true},"description":"Override values"}},"required":["template_helm_source_id","values"]},"minItems":1}},"required":["updates"]}}}},"responses":{"202":{"description":"Update workflow started"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Install not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/installs/{id}/logs":{"get":{"tags":["Installs"],"summary":"Stream install logs","description":"Streams logs from the install pods via Server-Sent Events. Use the `Accept: text/event-stream` header.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Install ID"},"required":true,"description":"Install ID","name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":253,"description":"Pod name (all pods if omitted)"},"required":false,"description":"Pod name (all pods if omitted)","name":"pod","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":253,"description":"Container name"},"required":false,"description":"Container name","name":"container","in":"query"},{"schema":{"type":"boolean","nullable":true,"default":true,"description":"Follow log output"},"required":false,"description":"Follow log output","name":"follow","in":"query"},{"schema":{"type":"integer","nullable":true,"description":"Lines to tail"},"required":false,"description":"Lines to tail","name":"tail","in":"query"},{"schema":{"type":"integer","nullable":true,"default":0,"description":"Only return logs newer than this many seconds"},"required":false,"description":"Only return logs newer than this many seconds","name":"since_seconds","in":"query"}],"responses":{"200":{"description":"Log stream (text/event-stream)"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Install not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/regions":{"get":{"tags":["Regions"],"summary":"List regions in workspace","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of regions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegionList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Regions"],"summary":"Create region","security":[{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"example":"us-east-1"},"icon":{"type":"string","maxLength":256,"format":"uri","description":"Icon URL"}},"required":["name"]}}}},"responses":{"201":{"description":"Region created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Region"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/registry/credentials":{"get":{"tags":["Registry"],"summary":"List registry credentials","description":"Returns credentials for the workspace. Credential secrets are never exposed.","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Items per page (1-100)","example":50},"required":false,"description":"Items per page (1-100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegistryCredentialList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Registry"],"summary":"Create registry credential","description":"Adds a registry credential to the workspace for OCI proxy authentication.","security":[{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"example":"GitHub Container Registry"},"registry_url":{"type":"string","minLength":1,"maxLength":2048,"example":"ghcr.io"},"type":{"type":"string","enum":["basic","token","oauth"],"description":"Auth type"},"credentials":{"type":"object","properties":{"username":{"type":"string","minLength":1,"maxLength":200},"password":{"type":"string","minLength":1,"maxLength":500},"token":{"type":"string","minLength":1,"maxLength":2000},"client_id":{"type":"string","minLength":1,"maxLength":200},"client_secret":{"type":"string","minLength":1,"maxLength":500},"token_url":{"type":"string","minLength":1,"maxLength":2048}},"description":"Auth credentials (type-dependent)"}},"required":["name","registry_url","type","credentials"]}}}},"responses":{"201":{"description":"Credential created"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing or invalid workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/registry/credentials/{id}":{"delete":{"tags":["Registry"],"summary":"Delete registry credential","security":[{"BearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Credential ID"},"required":true,"description":"Credential ID","name":"id","in":"path"}],"responses":{"204":{"description":"Credential deleted"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Credential not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}} \ No newline at end of file diff --git a/internal/cmd/auth/auth.go b/internal/cmd/auth/auth.go index d6280b7..cedace4 100644 --- a/internal/cmd/auth/auth.go +++ b/internal/cmd/auth/auth.go @@ -39,7 +39,7 @@ and stores a session token. Sessions are long-lived and auto-refresh on use. With --token, stores the given token directly (PAT or session token). -Create PATs at https://dash.cnap.tech/settings/tokens`, +Create PATs at https://cnap.tech/settings/tokens`, RunE: func(cmd *cobra.Command, args []string) error { cfg, err := config.Load() if err != nil { diff --git a/internal/config/config.go b/internal/config/config.go index 5305bcc..973a520 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -10,7 +10,7 @@ import ( const ( DefaultAPIURL = "https://api.cnap.tech" - DefaultAuthURL = "https://dash.cnap.tech" + DefaultAuthURL = "https://cnap.tech" configDir = ".cnap" configFile = "config.yaml" )