From 1ad5e59646e91e80889cfe84ae3031db2b6d063f Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Thu, 13 Aug 2026 11:59:15 -0700 Subject: [PATCH 01/12] Extract the OpenAPI docs generator into a shared module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DoltHub v2 generator was one 448-line script. Hosted's v1 API needs the same rendering — its spec deliberately shares DoltHub v2's error model, success envelope, and pagination conventions — so the rendering moves to scripts/lib/openapi-docs.mjs and generate-api-v2.mjs becomes config. The only DoltHub-specific behaviour left is the Database tag's sub-resource grouping, now an opt-in `groups` config rather than `if (tag === "Database")`. Two defects fixed along the way, which is why the v2 pages change: Schema links resolved one level too deep. Pages build to /index.html and are served from /, so a sibling-relative `models#model-user` from the user page resolved to .../user/models — dead on every generated v2 page, and in the hand-written authentication.md and migration.md too. Links are now site-root-relative; the rehype base-path plugin prefixes the site base. curl examples dropped required query parameters, so the command as printed was a 400. The SQL endpoints need ?ref=&q=; they now carry the spec's own example values. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/generate-api-v2.mjs | 482 +++-------------- scripts/lib/openapi-docs.mjs | 485 ++++++++++++++++++ .../products/dolthub/api/v2/authentication.md | 2 +- .../products/dolthub/api/v2/database.md | 302 +++++------ .../products/dolthub/api/v2/migration.md | 58 +-- .../products/dolthub/api/v2/operations.md | 24 +- .../content/products/dolthub/api/v2/user.md | 8 +- 7 files changed, 750 insertions(+), 611 deletions(-) create mode 100644 scripts/lib/openapi-docs.mjs diff --git a/scripts/generate-api-v2.mjs b/scripts/generate-api-v2.mjs index 41e349d..dbdee42 100644 --- a/scripts/generate-api-v2.mjs +++ b/scripts/generate-api-v2.mjs @@ -11,437 +11,91 @@ * operations.md — Operations-tagged endpoints * models.md — all component schemas * - * authentication.md and README.md are hand-written; this script does not touch them. + * authentication.md, migration.md, and README.md are hand-written; this script + * does not touch them. + * + * The rendering lives in scripts/lib/openapi-docs.mjs, shared with the Hosted + * v1 generator. */ -import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs"; -import { join, dirname } from "path"; +import { dirname, join } from "path"; import { fileURLToPath } from "url"; -import yaml from "yaml"; +import { generateApiDocs } from "./lib/openapi-docs.mjs"; const __dirname = dirname(fileURLToPath(import.meta.url)); -const SPEC_PATH = join(__dirname, "../specs/dolthub-v2.yaml"); -const OUT_DIR = join( - __dirname, - "../site/dolt/src/content/products/dolthub/api/v2" -); - -const spec = yaml.parse(readFileSync(SPEC_PATH, "utf-8")); - -// --------------------------------------------------------------------------- -// $ref resolution (with cycle guard) -// --------------------------------------------------------------------------- - -function resolveRef(ref) { - const parts = ref.replace(/^#\//, "").split("/"); - let obj = spec; - for (const p of parts) obj = obj[p]; - return obj; -} - -function deref(obj, depth = 0) { - if (depth > 8) return obj; // cycle guard - if (typeof obj !== "object" || obj === null) return obj; - if (Array.isArray(obj)) return obj.map((x) => deref(x, depth + 1)); - if ("$ref" in obj) return deref(resolveRef(obj.$ref), depth + 1); - return Object.fromEntries( - Object.entries(obj).map(([k, v]) => [k, deref(v, depth + 1)]) - ); -} - -function refName(refStr) { - return refStr?.split("/").pop(); -} - -// --------------------------------------------------------------------------- -// Formatting helpers -// --------------------------------------------------------------------------- - -const METHOD_LABELS = { - get: "GET", - post: "POST", - put: "PUT", - patch: "PATCH", - delete: "DELETE", -}; - -function escapeMarkdown(str = "") { - return str.replace(/\|/g, "\\|").replace(/\n+/g, " ").trim(); -} - -// Same palette + markup as the hand-written v1alpha1 pages' .api-method / -// .api-path spans (see DocsLayout.astro), so v2 endpoint headers match. -function methodSpan(method) { - const colors = { - get: "29E3C1", - post: "6DB0FC", - patch: "F0A35C", - put: "F0A35C", - delete: "EF5350", - }; - const color = colors[method] ?? "888888"; - const label = METHOD_LABELS[method] ?? method.toUpperCase(); - return `${label}`; -} - -function curlExample(method, path, operation) { - const lines = [ - `curl -X ${METHOD_LABELS[method] ?? method.toUpperCase()} 'https://www.dolthub.com${path}'`, - ` -H 'Authorization: Bearer YOUR_TOKEN'`, - ]; - if (method !== "get" && method !== "delete") { - lines.push(` -H 'Content-Type: application/json'`); - const reqBody = operation.requestBody?.content?.["application/json"]?.schema; - if (reqBody) { - const resolved = deref(reqBody); - const required = resolved.required ?? []; - const props = resolved.properties ?? {}; - const example = Object.fromEntries( - Object.entries(props) - .filter(([k]) => required.includes(k)) - .slice(0, 4) - .map(([k, v]) => { - const t = v.type ?? (v.enum ? "enum" : "object"); - const ex = - v.examples?.[0] ?? - v.example ?? - (t === "string" ? `"example_${k}"` : t === "boolean" ? false : 0); - return [k, ex]; - }) - ); - if (Object.keys(example).length > 0) { - lines.push(` -d '${JSON.stringify(example)}'`); - } - } - } - return lines.join(" \\\n"); -} - -function parametersSection(params) { - if (!params?.length) return ""; - const rows = params - .map((p) => { - const resolved = deref(p); - const location = resolved.in ?? ""; - const name = resolved.name ?? ""; - const required = resolved.required ? "yes" : "no"; - const type = resolved.schema?.type ?? ""; - const desc = escapeMarkdown(resolved.description ?? ""); - return `| \`${name}\` | ${location} | ${type} | ${required} | ${desc} |`; - }) - .join("\n"); - return `\n**Parameters**\n\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n${rows}\n`; -} - -function requestBodySection(requestBody) { - if (!requestBody) return ""; - const schema = requestBody.content?.["application/json"]?.schema; - if (!schema) return ""; - const resolved = deref(schema); - const required = resolved.required ?? []; - const props = resolved.properties ?? {}; - if (!Object.keys(props).length) return ""; - const rows = Object.entries(props) - .map(([k, v]) => { - const req = required.includes(k) ? "yes" : "no"; - const type = v.type ?? (v.$ref ? refName(v.$ref) : "object"); - const desc = escapeMarkdown(v.description ?? ""); - return `| \`${k}\` | ${type} | ${req} | ${desc} |`; - }) - .join("\n"); - return `\n**Request body**\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n${rows}\n`; -} - -function responseSchemaName(schema) { - if (!schema) return ""; - if (schema.$ref) return refName(schema.$ref); - // Common pattern: allOf: [Envelope, { properties: { data: $ref Schema } }] - if (schema.allOf) { - for (const s of schema.allOf) { - if (s.properties?.data?.$ref) return refName(s.properties.data.$ref); - // List responses: data.items.$ref - if (s.properties?.data?.items?.$ref) - return refName(s.properties.data.items.$ref) + "[]"; - } - } - return ""; -} - -function successExampleBlock(rawResponses) { - if (!rawResponses) return ""; - const successCode = Object.keys(rawResponses).find((c) => /^2/.test(c)); - if (!successCode) return ""; - - const rawResp = rawResponses[successCode]; - const resp = rawResp.$ref ? resolveRef(rawResp.$ref) : rawResp; - const schema = resp.content?.["application/json"]?.schema; - if (!schema?.allOf) return ""; - - let dataExample = null; - let isList = false; - - for (const s of schema.allOf) { - if (s.properties?.data?.$ref) { - const name = refName(s.properties.data.$ref); - dataExample = spec.components?.schemas?.[name]?.examples?.[0] ?? null; - break; - } - if (s.properties?.data?.type === "array" && s.properties?.data?.items?.$ref) { - const name = refName(s.properties.data.items.$ref); - dataExample = spec.components?.schemas?.[name]?.examples?.[0] ?? null; - isList = true; - break; - } - } - - if (!dataExample) return ""; - - const body = isList - ? { data: [dataExample], meta: { next_page_token: "eyJvZmZzZXQiOjI1fQ" } } - : { data: dataExample }; - - return `\n**Example response \`${successCode}\`**\n\n\`\`\`json\n${JSON.stringify(body, null, 2)}\n\`\`\`\n`; -} - -function responsesSection(rawResponses) { - if (!rawResponses) return ""; - const rows = Object.entries(rawResponses) - .map(([code, rawResp]) => { - // Resolve top-level $ref (e.g. $ref: "#/components/responses/Unauthorized") - const resp = rawResp.$ref ? resolveRef(rawResp.$ref) : rawResp; - const desc = escapeMarkdown(resp.description ?? ""); - const schema = resp.content?.["application/json"]?.schema; - const name = responseSchemaName(schema); - const schemaLink = name - ? `[\`${name}\`](models#model-${name.replace("[]", "").toLowerCase()})` - : ""; - return `| \`${code}\` | ${desc} | ${schemaLink} |`; - }) - .join("\n"); - return `\n**Responses**\n\n| Status | Description | Schema |\n|--------|-------------|--------|\n${rows}\n`; -} - -// --------------------------------------------------------------------------- -// Derive sub-resource label from path (for grouping within the Database page) -// --------------------------------------------------------------------------- -function subResource(path) { +// The Database tag covers every database sub-resource, so its page gets an H2 +// per sub-resource rather than one flat list of ~20 endpoints. +function databaseSubResource(path) { // After /databases/{owner}/{database}/... - const m = path.match( - /^\/api\/v2\/databases\/\{[^}]+\}\/\{[^}]+\}\/([^/]+)/ - ); + const m = path.match(/^\/api\/v2\/databases\/\{[^}]+\}\/\{[^}]+\}\/([^/]+)/); if (m) { switch (m[1]) { - case "branches": return "Branches"; - case "tags": return "Tags"; - case "forks": return "Forks"; - case "releases": return "Releases"; + case "branches": + return "Branches"; + case "tags": + return "Tags"; + case "forks": + return "Forks"; + case "releases": + return "Releases"; case "sql": case "sql-writes": return "SQL"; - case "pulls": return "Pull Requests"; - case "imports": return "Imports"; - default: return m[1]; + case "pulls": + return "Pull Requests"; + case "imports": + return "Imports"; + default: + return m[1]; } } if (path.startsWith("/api/v2/databases")) return "Databases"; return "Other"; } -// --------------------------------------------------------------------------- -// Build per-endpoint Markdown block -// --------------------------------------------------------------------------- - -function endpointBlock(method, path, operation, headingLevel = "###") { - const anchor = `{#${operation.operationId}}`; - const title = operation.summary - ? operation.summary.replace(/\.$/, "") - : `${METHOD_LABELS[method]} ${path}`; - const heading = `${headingLevel} ${title} ${anchor}\n`; - const methodPath = `${methodSpan(method)} ${path}\n\n`; - const description = - operation.description && - operation.description.trim() !== (operation.summary ?? "").trim() - ? `${operation.description.trim()}\n\n` - : ""; - const params = parametersSection(operation.parameters); - const body = requestBodySection(deref(operation.requestBody ?? {})); - const responses = responsesSection(operation.responses); - const successExample = successExampleBlock(operation.responses); - const curl = `\n**Example request**\n\n\`\`\`sh\n${curlExample(method, path, operation)}\n\`\`\`\n`; - return [heading, methodPath, description, params, body, curl, responses, successExample] - .filter(Boolean) - .join(""); -} - -// --------------------------------------------------------------------------- -// Collect operations by tag -// --------------------------------------------------------------------------- - -const byTag = {}; // tag → [{ method, path, operation }] - -for (const [path, pathItem] of Object.entries(spec.paths ?? {})) { - for (const method of [ - "get", - "post", - "put", - "patch", - "delete", - "head", - "options", - ]) { - const op = pathItem[method]; - if (!op) continue; - for (const tag of op.tags ?? ["Untagged"]) { - if (!byTag[tag]) byTag[tag] = []; - byTag[tag].push({ method, path, operation: op }); - } - } -} - -// --------------------------------------------------------------------------- -// Tag-level page generators -// --------------------------------------------------------------------------- - -function generateTagPage(tag, items, frontmatter) { - const tagInfo = spec.tags?.find((t) => t.name === tag) ?? {}; - const intro = tagInfo.description ? `${tagInfo.description}\n\n` : ""; - - // For Database tag, group by sub-resource - if (tag === "Database") { - const groups = {}; - for (const item of items) { - const g = subResource(item.path); - if (!groups[g]) groups[g] = []; - groups[g].push(item); - } - const order = [ - "Databases", - "SQL", - "Branches", - "Tags", - "Forks", - "Releases", - "Pull Requests", - "Imports", - ]; - const sorted = [ - ...order.filter((g) => groups[g]), - ...Object.keys(groups).filter((g) => !order.includes(g)), - ]; - const sections = sorted - .map((g) => { - const endpoints = groups[g] - .map((item) => - endpointBlock(item.method, item.path, item.operation, "###") - ) - .join("\n---\n\n"); - return `## ${g}\n\n${endpoints}`; - }) - .join("\n\n"); - return `${frontmatter}\n\n${intro}${sections}\n`; - } - - // Other tags: flat list - const content = items - .map((item) => - endpointBlock(item.method, item.path, item.operation, "##") - ) - .join("\n---\n\n"); - return `${frontmatter}\n\n${intro}${content}\n`; -} - -// --------------------------------------------------------------------------- -// Models page -// --------------------------------------------------------------------------- - -function schemaBlock(name, schema) { - const anchor = `{#model-${name.toLowerCase()}}`; - const heading = `## ${name} ${anchor}\n`; - const desc = schema.description ? `${schema.description.trim()}\n\n` : ""; - - if (schema.enum) { - const descs = schema["x-enum-descriptions"] ?? []; - const hasDescs = descs.some(Boolean); - const values = schema.enum - .map((v, i) => - hasDescs - ? `| \`${v}\` | ${escapeMarkdown(descs[i] ?? "")} |` - : `| \`${v}\` |` - ) - .join("\n"); - const header = hasDescs - ? `| Value | Description |\n|-------|-------------|` - : `| Value |\n|-------|`; - return `${heading}${desc}**Enum values**\n\n${header}\n${values}\n`; - } - - const props = schema.properties ?? schema.allOf?.find((s) => s.properties)?.properties; - if (!props || !Object.keys(props).length) { - const type = schema.type ?? (schema.allOf ? "object" : ""); - return `${heading}${desc}${type ? `_Type: \`${type}\`_\n` : ""}\n`; - } - - const required = schema.required ?? schema.allOf?.find((s) => s.required)?.required ?? []; - const rows = Object.entries(props) - .map(([k, v]) => { - const req = required.includes(k) ? "yes" : "no"; - const type = v.type ?? (v.$ref ? refName(v.$ref) : v.allOf ? "object" : "object"); - const d = escapeMarkdown(v.description ?? ""); - return `| \`${k}\` | \`${type}\` | ${req} | ${d} |`; - }) - .join("\n"); - - return `${heading}${desc}| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n${rows}\n`; -} - -function generateModelsPage() { - const fm = `---\ntitle: "Models"\ndescription: Request and response schemas for the DoltHub v2 API.\n---\n\n# Models`; - const schemas = Object.entries(spec.components?.schemas ?? {}); - const blocks = schemas - .map(([name, schema]) => schemaBlock(name, deref(schema))) - .join("\n---\n\n"); - return `${fm}\n\nShared request and response types used across the v2 API. See the [error model](#model-problem) for how failures are reported.\n\n${blocks}\n`; -} - -// --------------------------------------------------------------------------- -// Write output files -// --------------------------------------------------------------------------- - -mkdirSync(OUT_DIR, { recursive: true }); - -const tagPages = [ - { - tag: "User", - file: "user.md", - frontmatter: - '---\ntitle: "User"\ndescription: The authenticated user resource in the DoltHub v2 API.\n---\n\n# User', - }, - { - tag: "Database", - file: "database.md", - frontmatter: - '---\ntitle: "Database"\ndescription: DoltHub databases, branches, tags, forks, releases, SQL, pull requests, and imports.\n---\n\n# Database', - }, - { - tag: "Operations", - file: "operations.md", +generateApiDocs({ + specPath: join(__dirname, "../specs/dolthub-v2.yaml"), + outDir: join(__dirname, "../site/dolt/src/content/products/dolthub/api/v2"), + tagPages: [ + { + tag: "User", + file: "user.md", + frontmatter: + '---\ntitle: "User"\ndescription: The authenticated user resource in the DoltHub v2 API.\n---\n\n# User', + }, + { + tag: "Database", + file: "database.md", + frontmatter: + '---\ntitle: "Database"\ndescription: DoltHub databases, branches, tags, forks, releases, SQL, pull requests, and imports.\n---\n\n# Database', + groups: { + of: databaseSubResource, + order: [ + "Databases", + "SQL", + "Branches", + "Tags", + "Forks", + "Releases", + "Pull Requests", + "Imports", + ], + }, + }, + { + tag: "Operations", + file: "operations.md", + frontmatter: + '---\ntitle: "Operations"\ndescription: Long-running async operations in the DoltHub v2 API.\n---\n\n# Operations', + }, + ], + models: { + file: "models.md", + href: "/products/dolthub/api/v2/models", frontmatter: - '---\ntitle: "Operations"\ndescription: Long-running async operations in the DoltHub v2 API.\n---\n\n# Operations', + '---\ntitle: "Models"\ndescription: Request and response schemas for the DoltHub v2 API.\n---\n\n# Models', + intro: + "Shared request and response types used across the v2 API. See the [error model](#model-problem) for how failures are reported.", }, -]; - -for (const { tag, file, frontmatter } of tagPages) { - const items = byTag[tag] ?? []; - if (!items.length) { - console.warn(`Warning: no endpoints found for tag "${tag}"`); - continue; - } - const content = generateTagPage(tag, items, frontmatter); - writeFileSync(join(OUT_DIR, file), content); - console.log(`Wrote ${file} (${items.length} endpoints)`); -} - -const modelsContent = generateModelsPage(); -writeFileSync(join(OUT_DIR, "models.md"), modelsContent); -console.log(`Wrote models.md (${Object.keys(spec.components?.schemas ?? {}).length} schemas)`); +}); diff --git a/scripts/lib/openapi-docs.mjs b/scripts/lib/openapi-docs.mjs new file mode 100644 index 0000000..a5cf870 --- /dev/null +++ b/scripts/lib/openapi-docs.mjs @@ -0,0 +1,485 @@ +/** + * Shared OpenAPI → Markdown docs generator. + * + * Both the DoltHub v2 API and the Hosted v1 API are defined by OpenAPI 3.1 + * specs that share an error model (RFC 9457 `Problem`), a success `Envelope`, + * and cursor pagination — deliberately, so the two public APIs don't disagree + * about the basics. This module holds the rendering that follows from that + * shared shape; the per-API scripts supply only what genuinely differs (spec + * path, output directory, page list, and any tag-level grouping). + * + * See scripts/generate-api-v2.mjs and scripts/generate-hosted-api-v1.mjs. + */ + +import { readFileSync, writeFileSync, mkdirSync } from "fs"; +import { join } from "path"; +import yaml from "yaml"; + +const METHOD_LABELS = { + get: "GET", + post: "POST", + put: "PUT", + patch: "PATCH", + delete: "DELETE", +}; + +// Same palette + markup as the hand-written v1alpha1 pages' .api-method / +// .api-path spans (see site/shared/layouts/DocsLayout.astro), so generated +// endpoint headers match the hand-written ones. +const METHOD_COLORS = { + get: "29E3C1", + post: "6DB0FC", + patch: "F0A35C", + put: "F0A35C", + delete: "EF5350", +}; + +const HTTP_METHODS = [ + "get", + "post", + "put", + "patch", + "delete", + "head", + "options", +]; + +function escapeMarkdown(str = "") { + return str.replace(/\|/g, "\\|").replace(/\n+/g, " ").trim(); +} + +function refName(refStr) { + return refStr?.split("/").pop(); +} + +function methodSpan(method) { + const color = METHOD_COLORS[method] ?? "888888"; + const label = METHOD_LABELS[method] ?? method.toUpperCase(); + return `${label}`; +} + +/** + * Builds a renderer bound to one parsed spec. Everything that needs to walk + * `$ref`s closes over `spec`, so nothing is passed around explicitly. + */ +function createRenderer(spec, { baseUrl, tokenPlaceholder, modelsHref }) { + // ------------------------------------------------------------------------- + // $ref resolution (with cycle guard) + // ------------------------------------------------------------------------- + + function resolveRef(ref) { + const parts = ref.replace(/^#\//, "").split("/"); + let obj = spec; + for (const p of parts) obj = obj[p]; + return obj; + } + + function deref(obj, depth = 0) { + if (depth > 8) return obj; // cycle guard + if (typeof obj !== "object" || obj === null) return obj; + if (Array.isArray(obj)) return obj.map((x) => deref(x, depth + 1)); + if ("$ref" in obj) return deref(resolveRef(obj.$ref), depth + 1); + return Object.fromEntries( + Object.entries(obj).map(([k, v]) => [k, deref(v, depth + 1)]) + ); + } + + // ------------------------------------------------------------------------- + // Examples + // ------------------------------------------------------------------------- + + // OpenAPI 3.1 permits a media type to carry named `examples`. Prefer the one + // named `default`, else the first — a hand-written example beats anything + // synthesized from the schema. + function mediaTypeExample(mediaType) { + if (!mediaType) return undefined; + const named = mediaType.examples; + if (named && typeof named === "object") { + const entry = named.default ?? Object.values(named)[0]; + if (entry && "value" in entry) return entry.value; + } + return mediaType.example; + } + + function requestExample(operation) { + const media = operation.requestBody?.content?.["application/json"]; + const authored = mediaTypeExample(media); + if (authored !== undefined) return authored; + + // Fall back to a minimal object built from the required properties. + const schema = media?.schema; + if (!schema) return undefined; + const resolved = deref(schema); + const required = resolved.required ?? []; + const props = resolved.properties ?? {}; + const example = Object.fromEntries( + Object.entries(props) + .filter(([k]) => required.includes(k)) + .slice(0, 4) + .map(([k, v]) => { + const t = v.type ?? (v.enum ? "enum" : "object"); + const ex = + v.examples?.[0] ?? + v.example ?? + (t === "string" ? `"example_${k}"` : t === "boolean" ? false : 0); + return [k, ex]; + }) + ); + return Object.keys(example).length ? example : undefined; + } + + // A value to stand in for a parameter in a curl example: whatever the spec + // documents, else the first enum member, else the parameter's own name. + function parameterExample(param) { + return ( + param.example ?? + param.schema?.examples?.[0] ?? + param.schema?.example ?? + param.schema?.enum?.[0] ?? + param.name + ); + } + + // Required query parameters belong in the example URL — without them the + // command as printed is a 400 rather than something a reader can paste. + // Optional ones are left out so the example shows the minimal call. + function requiredQueryString(parameters) { + const required = (parameters ?? []) + .map((p) => deref(p)) + .filter((p) => p.in === "query" && p.required); + if (!required.length) return ""; + const pairs = required.map( + (p) => + `${encodeURIComponent(p.name)}=${encodeURIComponent(String(parameterExample(p)))}` + ); + return `?${pairs.join("&")}`; + } + + function curlExample(method, path, operation) { + const url = `${baseUrl}${path}${requiredQueryString(operation.parameters)}`; + const lines = [ + `curl -X ${METHOD_LABELS[method] ?? method.toUpperCase()} '${url}'`, + ` -H 'Authorization: Bearer ${tokenPlaceholder}'`, + ]; + if (method !== "get" && method !== "delete") { + lines.push(` -H 'Content-Type: application/json'`); + const example = requestExample(operation); + if (example !== undefined) { + lines.push(` -d '${JSON.stringify(example)}'`); + } + } + return lines.join(" \\\n"); + } + + function successExampleBlock(rawResponses) { + if (!rawResponses) return ""; + const successCode = Object.keys(rawResponses).find((c) => /^2/.test(c)); + if (!successCode) return ""; + + const rawResp = rawResponses[successCode]; + const resp = rawResp.$ref ? resolveRef(rawResp.$ref) : rawResp; + const media = resp.content?.["application/json"]; + + // An example authored on the response wins outright — it shows the whole + // envelope, including any `meta`, exactly as the API returns it. + const authored = mediaTypeExample(media); + if (authored !== undefined) { + return exampleBlock(successCode, authored); + } + + // Otherwise synthesize the envelope from the schema the endpoint narrows + // `data` to: allOf: [Envelope, { properties: { data: $ref Schema } }]. + const schema = media?.schema; + if (!schema?.allOf) return ""; + + let dataExample = null; + let isList = false; + + for (const s of schema.allOf) { + if (s.properties?.data?.$ref) { + const name = refName(s.properties.data.$ref); + dataExample = spec.components?.schemas?.[name]?.examples?.[0] ?? null; + break; + } + if ( + s.properties?.data?.type === "array" && + s.properties?.data?.items?.$ref + ) { + const name = refName(s.properties.data.items.$ref); + dataExample = spec.components?.schemas?.[name]?.examples?.[0] ?? null; + isList = true; + break; + } + } + + if (!dataExample) return ""; + + const body = isList + ? { data: [dataExample], meta: { next_page_token: "eyJvZmZzZXQiOjI1fQ" } } + : { data: dataExample }; + + return exampleBlock(successCode, body); + } + + function exampleBlock(code, body) { + return `\n**Example response \`${code}\`**\n\n\`\`\`json\n${JSON.stringify(body, null, 2)}\n\`\`\`\n`; + } + + // ------------------------------------------------------------------------- + // Endpoint sections + // ------------------------------------------------------------------------- + + function parametersSection(params) { + if (!params?.length) return ""; + const rows = params + .map((p) => { + const resolved = deref(p); + const location = resolved.in ?? ""; + const name = resolved.name ?? ""; + const required = resolved.required ? "yes" : "no"; + const type = resolved.schema?.type ?? ""; + const desc = escapeMarkdown(resolved.description ?? ""); + return `| \`${name}\` | ${location} | ${type} | ${required} | ${desc} |`; + }) + .join("\n"); + return `\n**Parameters**\n\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n${rows}\n`; + } + + function requestBodySection(requestBody) { + if (!requestBody) return ""; + const schema = requestBody.content?.["application/json"]?.schema; + if (!schema) return ""; + const resolved = deref(schema); + const required = resolved.required ?? []; + const props = resolved.properties ?? {}; + if (!Object.keys(props).length) return ""; + const rows = Object.entries(props) + .map(([k, v]) => { + const req = required.includes(k) ? "yes" : "no"; + const type = v.type ?? (v.$ref ? refName(v.$ref) : "object"); + const desc = escapeMarkdown(v.description ?? ""); + return `| \`${k}\` | ${type} | ${req} | ${desc} |`; + }) + .join("\n"); + return `\n**Request body**\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n${rows}\n`; + } + + function responseSchemaName(schema) { + if (!schema) return ""; + if (schema.$ref) return refName(schema.$ref); + // Common pattern: allOf: [Envelope, { properties: { data: $ref Schema } }] + if (schema.allOf) { + for (const s of schema.allOf) { + if (s.properties?.data?.$ref) return refName(s.properties.data.$ref); + // List responses: data.items.$ref + if (s.properties?.data?.items?.$ref) + return refName(s.properties.data.items.$ref) + "[]"; + } + } + return ""; + } + + function responsesSection(rawResponses) { + if (!rawResponses) return ""; + const rows = Object.entries(rawResponses) + .map(([code, rawResp]) => { + // Resolve top-level $ref (e.g. $ref: "#/components/responses/Unauthorized") + const resp = rawResp.$ref ? resolveRef(rawResp.$ref) : rawResp; + const desc = escapeMarkdown(resp.description ?? ""); + const schema = resp.content?.["application/json"]?.schema; + const name = responseSchemaName(schema); + const schemaLink = name + ? `[\`${name}\`](${modelsHref}#model-${name.replace("[]", "").toLowerCase()})` + : ""; + return `| \`${code}\` | ${desc} | ${schemaLink} |`; + }) + .join("\n"); + return `\n**Responses**\n\n| Status | Description | Schema |\n|--------|-------------|--------|\n${rows}\n`; + } + + function endpointBlock(method, path, operation, headingLevel = "###") { + const anchor = `{#${operation.operationId}}`; + const title = operation.summary + ? operation.summary.replace(/\.$/, "") + : `${METHOD_LABELS[method]} ${path}`; + const heading = `${headingLevel} ${title} ${anchor}\n`; + const methodPath = `${methodSpan(method)} ${path}\n\n`; + const description = + operation.description && + operation.description.trim() !== (operation.summary ?? "").trim() + ? `${operation.description.trim()}\n\n` + : ""; + const params = parametersSection(operation.parameters); + const body = requestBodySection(deref(operation.requestBody ?? {})); + const responses = responsesSection(operation.responses); + const successExample = successExampleBlock(operation.responses); + const curl = `\n**Example request**\n\n\`\`\`sh\n${curlExample(method, path, operation)}\n\`\`\`\n`; + return [ + heading, + methodPath, + description, + params, + body, + curl, + responses, + successExample, + ] + .filter(Boolean) + .join(""); + } + + // ------------------------------------------------------------------------- + // Models + // ------------------------------------------------------------------------- + + function schemaBlock(name, schema) { + const anchor = `{#model-${name.toLowerCase()}}`; + const heading = `## ${name} ${anchor}\n`; + const desc = schema.description ? `${schema.description.trim()}\n\n` : ""; + + if (schema.enum) { + const descs = schema["x-enum-descriptions"] ?? []; + const hasDescs = descs.some(Boolean); + const values = schema.enum + .map((v, i) => + hasDescs + ? `| \`${v}\` | ${escapeMarkdown(descs[i] ?? "")} |` + : `| \`${v}\` |` + ) + .join("\n"); + const header = hasDescs + ? `| Value | Description |\n|-------|-------------|` + : `| Value |\n|-------|`; + return `${heading}${desc}**Enum values**\n\n${header}\n${values}\n`; + } + + const props = + schema.properties ?? schema.allOf?.find((s) => s.properties)?.properties; + if (!props || !Object.keys(props).length) { + const type = schema.type ?? (schema.allOf ? "object" : ""); + return `${heading}${desc}${type ? `_Type: \`${type}\`_\n` : ""}\n`; + } + + const required = + schema.required ?? schema.allOf?.find((s) => s.required)?.required ?? []; + const rows = Object.entries(props) + .map(([k, v]) => { + const req = required.includes(k) ? "yes" : "no"; + const type = + v.type ?? (v.$ref ? refName(v.$ref) : v.allOf ? "object" : "object"); + const d = escapeMarkdown(v.description ?? ""); + return `| \`${k}\` | \`${type}\` | ${req} | ${d} |`; + }) + .join("\n"); + + return `${heading}${desc}| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n${rows}\n`; + } + + return { deref, endpointBlock, schemaBlock }; +} + +/** + * Renders per-tag Markdown pages plus a models page for one OpenAPI spec. + * + * @param {object} config + * @param {string} config.specPath Absolute path to the OpenAPI YAML. + * @param {string} config.outDir Absolute path to write pages into. + * @param {string} [config.baseUrl] Base URL for curl examples. Defaults to the spec's first server. + * @param {string} [config.tokenPlaceholder] Stand-in token in curl examples. + * @param {Array} config.tagPages [{ tag, file, frontmatter, groups? }] + * `groups` opts a tag into sub-resource sections: { order: string[], of: (path) => string }. + * @param {object} config.models { file, href, frontmatter, intro } + * `href` is the site-root-relative path of the models page, e.g. + * "/products/hosted/api/v1/models". It must be root-relative rather than a + * bare "models": these pages build to `/index.html` and are served + * from `/`, so a sibling-relative link would resolve one level too + * deep. The rehype base-path plugin prefixes the site base at build time. + */ +export function generateApiDocs(config) { + const { + specPath, + outDir, + tokenPlaceholder = "YOUR_TOKEN", + tagPages, + models, + } = config; + + const spec = yaml.parse(readFileSync(specPath, "utf-8")); + const baseUrl = config.baseUrl ?? spec.servers?.[0]?.url ?? ""; + const { deref, endpointBlock, schemaBlock } = createRenderer(spec, { + baseUrl, + tokenPlaceholder, + modelsHref: models.href, + }); + + // Collect operations by tag. + const byTag = {}; // tag → [{ method, path, operation }] + for (const [path, pathItem] of Object.entries(spec.paths ?? {})) { + for (const method of HTTP_METHODS) { + const op = pathItem[method]; + if (!op) continue; + for (const tag of op.tags ?? ["Untagged"]) { + (byTag[tag] ??= []).push({ method, path, operation: op }); + } + } + } + + function generateTagPage(tag, items, { frontmatter, groups }) { + const tagInfo = spec.tags?.find((t) => t.name === tag) ?? {}; + const intro = tagInfo.description ? `${tagInfo.description}\n\n` : ""; + + // Tags with many endpoints across sub-resources get an H2 per sub-resource + // and endpoints at H3; everything else is a flat list of H2 endpoints. + if (groups) { + const grouped = {}; + for (const item of items) { + (grouped[groups.of(item.path)] ??= []).push(item); + } + const sorted = [ + ...groups.order.filter((g) => grouped[g]), + ...Object.keys(grouped).filter((g) => !groups.order.includes(g)), + ]; + const sections = sorted + .map((g) => { + const endpoints = grouped[g] + .map((item) => + endpointBlock(item.method, item.path, item.operation, "###") + ) + .join("\n---\n\n"); + return `## ${g}\n\n${endpoints}`; + }) + .join("\n\n"); + return `${frontmatter}\n\n${intro}${sections}\n`; + } + + const content = items + .map((item) => endpointBlock(item.method, item.path, item.operation, "##")) + .join("\n---\n\n"); + return `${frontmatter}\n\n${intro}${content}\n`; + } + + mkdirSync(outDir, { recursive: true }); + + for (const page of tagPages) { + const items = byTag[page.tag] ?? []; + if (!items.length) { + console.warn(`Warning: no endpoints found for tag "${page.tag}"`); + continue; + } + writeFileSync( + join(outDir, page.file), + generateTagPage(page.tag, items, page) + ); + console.log(`Wrote ${page.file} (${items.length} endpoints)`); + } + + const schemas = Object.entries(spec.components?.schemas ?? {}); + const blocks = schemas + .map(([name, schema]) => schemaBlock(name, deref(schema))) + .join("\n---\n\n"); + writeFileSync( + join(outDir, models.file), + `${models.frontmatter}\n\n${models.intro}\n\n${blocks}\n` + ); + console.log(`Wrote ${models.file} (${schemas.length} schemas)`); +} diff --git a/site/dolt/src/content/products/dolthub/api/v2/authentication.md b/site/dolt/src/content/products/dolthub/api/v2/authentication.md index ecec084..ea69232 100644 --- a/site/dolt/src/content/products/dolthub/api/v2/authentication.md +++ b/site/dolt/src/content/products/dolthub/api/v2/authentication.md @@ -37,4 +37,4 @@ Most write operations and all operations on private databases require authentica Endpoints that require authentication will return `401 Unauthorized` if no valid credential is supplied, and `403 Forbidden` if the credential is valid but lacks permission. -See [Models → Problem](models#model-problem) for the error response format. +See [Models → Problem](/products/dolthub/api/v2/models#model-problem) for the error response format. diff --git a/site/dolt/src/content/products/dolthub/api/v2/database.md b/site/dolt/src/content/products/dolthub/api/v2/database.md index a51f377..0699152 100644 --- a/site/dolt/src/content/products/dolthub/api/v2/database.md +++ b/site/dolt/src/content/products/dolthub/api/v2/database.md @@ -37,13 +37,13 @@ curl -X POST 'https://www.dolthub.com/api/v2/databases' \ | Status | Description | Schema | |--------|-------------|--------| -| `201` | The newly-created database. | [`Database`](models#model-database) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `409` | The request conflicts with the current state of the resource (e.g. it already exists). | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `201` | The newly-created database. | [`Database`](/products/dolthub/api/v2/models#model-database) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `409` | The request conflicts with the current state of the resource (e.g. it already exists). | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `201`** @@ -88,12 +88,12 @@ curl -X GET 'https://www.dolthub.com/api/v2/databases/{owner}/{database}' \ | Status | Description | Schema | |--------|-------------|--------| -| `200` | The database's metadata. | [`Database`](models#model-database) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `200` | The database's metadata. | [`Database`](/products/dolthub/api/v2/models#model-database) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `200`** @@ -137,7 +137,7 @@ Public databases are readable without authentication; private databases require **Example request** ```sh -curl -X GET 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/sql' \ +curl -X GET 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/sql?ref=main&q=SELECT%20name%2C%20year%20FROM%20jails%20LIMIT%2010' \ -H 'Authorization: Bearer YOUR_TOKEN' ``` @@ -145,12 +145,12 @@ curl -X GET 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/sql' \ | Status | Description | Schema | |--------|-------------|--------| -| `200` | The query result. SQL-level errors live in `status` + `message`. | [`QueryResult`](models#model-queryresult) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `200` | The query result. SQL-level errors live in `status` + `message`. | [`QueryResult`](/products/dolthub/api/v2/models#model-queryresult) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `200`** @@ -228,12 +228,12 @@ curl -X POST 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/sql' \ | Status | Description | Schema | |--------|-------------|--------| -| `200` | The read query result. SQL-level errors live in `status` + `message`. | [`QueryResult`](models#model-queryresult) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `200` | The read query result. SQL-level errors live in `status` + `message`. | [`QueryResult`](/products/dolthub/api/v2/models#model-queryresult) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `200`** @@ -310,13 +310,13 @@ curl -X POST 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/sql-wr | Status | Description | Schema | |--------|-------------|--------| -| `202` | The write operation has been accepted and is queued. | [`OperationRef`](models#model-operationref) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `202` | The write operation has been accepted and is queued. | [`OperationRef`](/products/dolthub/api/v2/models#model-operationref) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | ## Branches @@ -346,12 +346,12 @@ curl -X GET 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/branche | Status | Description | Schema | |--------|-------------|--------| -| `200` | The database's branches. | [`Branch[]`](models#model-branch) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `200` | The database's branches. | [`Branch[]`](/products/dolthub/api/v2/models#model-branch) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `200`** @@ -405,14 +405,14 @@ curl -X POST 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/branch | Status | Description | Schema | |--------|-------------|--------| -| `201` | The newly-created branch. | [`Branch`](models#model-branch) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `409` | The request conflicts with the current state of the resource (e.g. it already exists). | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `201` | The newly-created branch. | [`Branch`](/products/dolthub/api/v2/models#model-branch) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `409` | The request conflicts with the current state of the resource (e.g. it already exists). | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `201`** @@ -454,12 +454,12 @@ curl -X GET 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/tags' \ | Status | Description | Schema | |--------|-------------|--------| -| `200` | The database's tags. | [`Tag[]`](models#model-tag) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `200` | The database's tags. | [`Tag[]`](/products/dolthub/api/v2/models#model-tag) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `200`** @@ -515,14 +515,14 @@ curl -X POST 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/tags' | Status | Description | Schema | |--------|-------------|--------| -| `201` | The newly-created tag. | [`Tag`](models#model-tag) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `409` | The request conflicts with the current state of the resource (e.g. it already exists). | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `201` | The newly-created tag. | [`Tag`](/products/dolthub/api/v2/models#model-tag) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `409` | The request conflicts with the current state of the resource (e.g. it already exists). | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `201`** @@ -564,12 +564,12 @@ curl -X GET 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/forks' | Status | Description | Schema | |--------|-------------|--------| -| `200` | The database's direct fork children (immediate forks only). | [`DatabaseRef[]`](models#model-databaseref) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `200` | The database's direct fork children (immediate forks only). | [`DatabaseRef[]`](/products/dolthub/api/v2/models#model-databaseref) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `200`** @@ -621,14 +621,14 @@ curl -X POST 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/forks' | Status | Description | Schema | |--------|-------------|--------| -| `202` | The fork operation has been accepted and is queued. | [`OperationRef`](models#model-operationref) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `422` | The request was well-formed but semantically invalid. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `202` | The fork operation has been accepted and is queued. | [`OperationRef`](/products/dolthub/api/v2/models#model-operationref) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `422` | The request was well-formed but semantically invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | ## Releases @@ -658,12 +658,12 @@ curl -X GET 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/release | Status | Description | Schema | |--------|-------------|--------| -| `200` | The database's releases. | [`Release[]`](models#model-release) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `200` | The database's releases. | [`Release[]`](/products/dolthub/api/v2/models#model-release) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `200`** @@ -723,14 +723,14 @@ curl -X POST 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/releas | Status | Description | Schema | |--------|-------------|--------| -| `201` | The newly-created release. | [`Release`](models#model-release) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `409` | The request conflicts with the current state of the resource (e.g. it already exists). | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `201` | The newly-created release. | [`Release`](/products/dolthub/api/v2/models#model-release) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `409` | The request conflicts with the current state of the resource (e.g. it already exists). | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `201`** @@ -775,12 +775,12 @@ curl -X GET 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/pulls' | Status | Description | Schema | |--------|-------------|--------| -| `200` | The database's pull requests. | [`PullSummary[]`](models#model-pullsummary) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `200` | The database's pull requests. | [`PullSummary[]`](/products/dolthub/api/v2/models#model-pullsummary) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `200`** @@ -839,14 +839,14 @@ curl -X POST 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/pulls' | Status | Description | Schema | |--------|-------------|--------| -| `201` | The newly-created pull request. | [`Pull`](models#model-pull) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `409` | The request conflicts with the current state of the resource (e.g. it already exists). | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `201` | The newly-created pull request. | [`Pull`](/products/dolthub/api/v2/models#model-pull) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `409` | The request conflicts with the current state of the resource (e.g. it already exists). | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `201`** @@ -904,12 +904,12 @@ curl -X GET 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/pulls/{ | Status | Description | Schema | |--------|-------------|--------| -| `200` | The requested pull request. | [`Pull`](models#model-pull) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `200` | The requested pull request. | [`Pull`](/products/dolthub/api/v2/models#model-pull) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `200`** @@ -976,13 +976,13 @@ curl -X PATCH 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/pulls | Status | Description | Schema | |--------|-------------|--------| -| `200` | The updated pull request. | [`Pull`](models#model-pull) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `200` | The updated pull request. | [`Pull`](/products/dolthub/api/v2/models#model-pull) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `200`** @@ -1040,12 +1040,12 @@ curl -X GET 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/pulls/{ | Status | Description | Schema | |--------|-------------|--------| -| `200` | The pull request's comments. | [`PullComment[]`](models#model-pullcomment) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `200` | The pull request's comments. | [`PullComment[]`](/products/dolthub/api/v2/models#model-pullcomment) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `200`** @@ -1101,13 +1101,13 @@ curl -X POST 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/pulls/ | Status | Description | Schema | |--------|-------------|--------| -| `201` | The newly-created comment. | [`PullComment`](models#model-pullcomment) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `201` | The newly-created comment. | [`PullComment`](/products/dolthub/api/v2/models#model-pullcomment) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `201`** @@ -1151,14 +1151,14 @@ curl -X POST 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/pulls/ | Status | Description | Schema | |--------|-------------|--------| -| `202` | The merge operation has been accepted and is queued. | [`OperationRef`](models#model-operationref) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `422` | The request was well-formed but semantically invalid. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `202` | The merge operation has been accepted and is queued. | [`OperationRef`](/products/dolthub/api/v2/models#model-operationref) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `422` | The request was well-formed but semantically invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | ## Imports @@ -1197,13 +1197,13 @@ curl -X POST 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/import | Status | Description | Schema | |--------|-------------|--------| -| `201` | The multipart upload session. | [`ImportUpload`](models#model-importupload) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `201` | The multipart upload session. | [`ImportUpload`](/products/dolthub/api/v2/models#model-importupload) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | --- @@ -1252,11 +1252,11 @@ curl -X POST 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/import | Status | Description | Schema | |--------|-------------|--------| -| `202` | The import operation has been accepted and is queued. | [`OperationRef`](models#model-operationref) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `202` | The import operation has been accepted and is queued. | [`OperationRef`](/products/dolthub/api/v2/models#model-operationref) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | diff --git a/site/dolt/src/content/products/dolthub/api/v2/migration.md b/site/dolt/src/content/products/dolthub/api/v2/migration.md index a21f328..fad5426 100644 --- a/site/dolt/src/content/products/dolthub/api/v2/migration.md +++ b/site/dolt/src/content/products/dolthub/api/v2/migration.md @@ -5,7 +5,7 @@ description: How to migrate your integrations from the DoltHub v1alpha1 API to v # Migrating from v1alpha1 -This page covers the mechanical changes needed to move an existing v1alpha1 integration to v2. Read it alongside the [v2 overview](../v2) and the per-resource pages. +This page covers the mechanical changes needed to move an existing v1alpha1 integration to v2. Read it alongside the [v2 overview](/products/dolthub/api/v2) and the per-resource pages. ## 1. Base URL @@ -54,7 +54,7 @@ v1alpha1 responses are flat and ad-hoc — each endpoint has its own top-level s ## 3. Error model -v1alpha1 error shapes vary by endpoint. v2 uses [RFC 9457 Problem Details](https://www.rfc-editor.org/rfc/rfc9457) for every non-2xx response. See [Models → Problem](models#model-problem). +v1alpha1 error shapes vary by endpoint. v2 uses [RFC 9457 Problem Details](https://www.rfc-editor.org/rfc/rfc9457) for every non-2xx response. See [Models → Problem](/products/dolthub/api/v2/models#model-problem). ```json { @@ -104,7 +104,7 @@ v2 uses a single unified protocol: any async mutation returns `202` with an `Ope } ``` -Poll `GET /api/v2/operations/{operation_id}` until `status` is `succeeded` or `failed`. See [Operations](operations#getOperation). +Poll `GET /api/v2/operations/{operation_id}` until `status` is `succeeded` or `failed`. See [Operations](/products/dolthub/api/v2/operations#getOperation). ```sh curl 'https://www.dolthub.com/api/v2/operations/owners/dolthub/repos/us-jails/jobs/abc123' \ @@ -117,29 +117,29 @@ curl 'https://www.dolthub.com/api/v2/operations/owners/dolthub/repos/us-jails/jo | v1alpha1 | v2 | |---|---| -| `GET /user` | [`GET /user`](user#getCurrentUser) | -| `POST /database` | [`POST /databases`](database#createDatabase) | -| `GET /{owner}/{database}?q=` | [`GET /databases/{owner}/{database}/sql?q=`](database#runSqlReadQuery) | -| `GET /{owner}/{database}/{ref}?q=` | [`GET /databases/{owner}/{database}/sql?q=&ref=`](database#runSqlReadQuery) | -| `POST /{owner}/{database}/write/{from_branch}/{to_branch}` | [`POST /databases/{owner}/{database}/sql-writes`](database#runSqlWriteQuery) | -| `GET /{owner}/{database}/write` | Poll [`GET /operations/{id}`](operations#getOperation) | -| `GET /{owner}/{database}/forks` | [`GET /databases/{owner}/{database}/forks`](database#listForks) | -| `GET /{owner}/{database}/branches` | [`GET /databases/{owner}/{database}/branches`](database#listBranches) | -| `POST /{owner}/{database}/branches` | [`POST /databases/{owner}/{database}/branches`](database#createBranch) | -| `GET /{owner}/{database}/pulls` | [`GET /databases/{owner}/{database}/pulls`](database#listPulls) | -| `POST /{owner}/{database}/pulls` | [`POST /databases/{owner}/{database}/pulls`](database#createPull) | -| `GET /{owner}/{database}/pulls/{id}` | [`GET /databases/{owner}/{database}/pulls/{pull_number}`](database#getPull) | -| `PATCH /{owner}/{database}/pulls/{id}` | [`PATCH /databases/{owner}/{database}/pulls/{pull_number}`](database#updatePull) | -| `POST /{owner}/{database}/pulls/{id}/comments` | [`POST /databases/{owner}/{database}/pulls/{pull_number}/comments`](database#createPullComment) | -| `POST /{owner}/{database}/pulls/{id}/merge` | [`POST /databases/{owner}/{database}/pulls/{pull_number}/merge`](database#mergePull) | -| `GET /{owner}/{database}/pulls/{id}/merge` | Poll [`GET /operations/{id}`](operations#getOperation) | -| `GET /{owner}/{database}/releases` | [`GET /databases/{owner}/{database}/releases`](database#listReleases) | -| `POST /{owner}/{database}/releases` | [`POST /databases/{owner}/{database}/releases`](database#createRelease) | -| `GET /{owner}/{database}/tags` | [`GET /databases/{owner}/{database}/tags`](database#listTags) | -| `POST /{owner}/{database}/tags` | [`POST /databases/{owner}/{database}/tags`](database#createTag) | -| `POST /{owner}/{database}/upload` | [`POST .../imports/uploads`](database#createImportUpload) then [`POST .../imports`](database#createImport) | -| `GET /{owner}/{database}/upload` | Poll [`GET /operations/{id}`](operations#getOperation) | -| `POST /fork` | [`POST /databases/{owner}/{database}/forks`](database#createFork) | -| `GET /fork` | Poll [`GET /operations/{id}`](operations#getOperation) | -| `GET /{owner}/{database}/jobs` | [`GET /databases/{owner}/{database}/operations`](operations#listOperations) | -| `GET /users/{username}/operations` | [`GET /databases/{owner}/{database}/operations`](operations#listOperations) | +| `GET /user` | [`GET /user`](/products/dolthub/api/v2/user#getCurrentUser) | +| `POST /database` | [`POST /databases`](/products/dolthub/api/v2/database#createDatabase) | +| `GET /{owner}/{database}?q=` | [`GET /databases/{owner}/{database}/sql?q=`](/products/dolthub/api/v2/database#runSqlReadQuery) | +| `GET /{owner}/{database}/{ref}?q=` | [`GET /databases/{owner}/{database}/sql?q=&ref=`](/products/dolthub/api/v2/database#runSqlReadQuery) | +| `POST /{owner}/{database}/write/{from_branch}/{to_branch}` | [`POST /databases/{owner}/{database}/sql-writes`](/products/dolthub/api/v2/database#runSqlWriteQuery) | +| `GET /{owner}/{database}/write` | Poll [`GET /operations/{id}`](/products/dolthub/api/v2/operations#getOperation) | +| `GET /{owner}/{database}/forks` | [`GET /databases/{owner}/{database}/forks`](/products/dolthub/api/v2/database#listForks) | +| `GET /{owner}/{database}/branches` | [`GET /databases/{owner}/{database}/branches`](/products/dolthub/api/v2/database#listBranches) | +| `POST /{owner}/{database}/branches` | [`POST /databases/{owner}/{database}/branches`](/products/dolthub/api/v2/database#createBranch) | +| `GET /{owner}/{database}/pulls` | [`GET /databases/{owner}/{database}/pulls`](/products/dolthub/api/v2/database#listPulls) | +| `POST /{owner}/{database}/pulls` | [`POST /databases/{owner}/{database}/pulls`](/products/dolthub/api/v2/database#createPull) | +| `GET /{owner}/{database}/pulls/{id}` | [`GET /databases/{owner}/{database}/pulls/{pull_number}`](/products/dolthub/api/v2/database#getPull) | +| `PATCH /{owner}/{database}/pulls/{id}` | [`PATCH /databases/{owner}/{database}/pulls/{pull_number}`](/products/dolthub/api/v2/database#updatePull) | +| `POST /{owner}/{database}/pulls/{id}/comments` | [`POST /databases/{owner}/{database}/pulls/{pull_number}/comments`](/products/dolthub/api/v2/database#createPullComment) | +| `POST /{owner}/{database}/pulls/{id}/merge` | [`POST /databases/{owner}/{database}/pulls/{pull_number}/merge`](/products/dolthub/api/v2/database#mergePull) | +| `GET /{owner}/{database}/pulls/{id}/merge` | Poll [`GET /operations/{id}`](/products/dolthub/api/v2/operations#getOperation) | +| `GET /{owner}/{database}/releases` | [`GET /databases/{owner}/{database}/releases`](/products/dolthub/api/v2/database#listReleases) | +| `POST /{owner}/{database}/releases` | [`POST /databases/{owner}/{database}/releases`](/products/dolthub/api/v2/database#createRelease) | +| `GET /{owner}/{database}/tags` | [`GET /databases/{owner}/{database}/tags`](/products/dolthub/api/v2/database#listTags) | +| `POST /{owner}/{database}/tags` | [`POST /databases/{owner}/{database}/tags`](/products/dolthub/api/v2/database#createTag) | +| `POST /{owner}/{database}/upload` | [`POST .../imports/uploads`](/products/dolthub/api/v2/database#createImportUpload) then [`POST .../imports`](/products/dolthub/api/v2/database#createImport) | +| `GET /{owner}/{database}/upload` | Poll [`GET /operations/{id}`](/products/dolthub/api/v2/operations#getOperation) | +| `POST /fork` | [`POST /databases/{owner}/{database}/forks`](/products/dolthub/api/v2/database#createFork) | +| `GET /fork` | Poll [`GET /operations/{id}`](/products/dolthub/api/v2/operations#getOperation) | +| `GET /{owner}/{database}/jobs` | [`GET /databases/{owner}/{database}/operations`](/products/dolthub/api/v2/operations#listOperations) | +| `GET /users/{username}/operations` | [`GET /databases/{owner}/{database}/operations`](/products/dolthub/api/v2/operations#listOperations) | diff --git a/site/dolt/src/content/products/dolthub/api/v2/operations.md b/site/dolt/src/content/products/dolthub/api/v2/operations.md index 9b40b69..0cc891b 100644 --- a/site/dolt/src/content/products/dolthub/api/v2/operations.md +++ b/site/dolt/src/content/products/dolthub/api/v2/operations.md @@ -32,12 +32,12 @@ curl -X GET 'https://www.dolthub.com/api/v2/databases/{owner}/{database}/operati | Status | Description | Schema | |--------|-------------|--------| -| `200` | The database's async operations. | [`Operation[]`](models#model-operation) | -| `400` | The request was malformed or failed input validation. | [`Problem`](models#model-problem) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `200` | The database's async operations. | [`Operation[]`](/products/dolthub/api/v2/models#model-operation) | +| `400` | The request was malformed or failed input validation. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | --- @@ -64,10 +64,10 @@ curl -X GET 'https://www.dolthub.com/api/v2/operations/{operation_id}' \ | Status | Description | Schema | |--------|-------------|--------| -| `200` | The current state of the operation. | [`Operation`](models#model-operation) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](models#model-problem) | -| `404` | The requested resource does not exist. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `200` | The current state of the operation. | [`Operation`](/products/dolthub/api/v2/models#model-operation) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `403` | Authenticated, but not permitted to perform this action. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `404` | The requested resource does not exist. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | diff --git a/site/dolt/src/content/products/dolthub/api/v2/user.md b/site/dolt/src/content/products/dolthub/api/v2/user.md index 62c0481..d204b4f 100644 --- a/site/dolt/src/content/products/dolthub/api/v2/user.md +++ b/site/dolt/src/content/products/dolthub/api/v2/user.md @@ -24,10 +24,10 @@ curl -X GET 'https://www.dolthub.com/api/v2/user' \ | Status | Description | Schema | |--------|-------------|--------| -| `200` | The authenticated user's profile. | [`User`](models#model-user) | -| `401` | Authentication credentials were missing or invalid. | [`Problem`](models#model-problem) | -| `405` | The HTTP method is not supported for this resource. | [`Problem`](models#model-problem) | -| `500` | An unexpected server error occurred. | [`Problem`](models#model-problem) | +| `200` | The authenticated user's profile. | [`User`](/products/dolthub/api/v2/models#model-user) | +| `401` | Authentication credentials were missing or invalid. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `405` | The HTTP method is not supported for this resource. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | +| `500` | An unexpected server error occurred. | [`Problem`](/products/dolthub/api/v2/models#model-problem) | **Example response `200`** From 5a5f94d952d72c48b0629668ab3c182dd3774b0e Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Thu, 13 Aug 2026 11:59:22 -0700 Subject: [PATCH 02/12] Add Hosted Dolt API v1 docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents the new public REST API for the Hosted control plane, under Products > Hosted Dolt > API > v1, mirroring the DoltHub API layout. specs/hosted-v1.yaml is vendored from ld's hosted-web/packages/hosted/openapi/v1.yaml, the same way the DoltHub v2 spec is. user.md, deployment.md, and models.md are generated from it by scripts/generate-hosted-api-v1.mjs (npm run generate-hosted-api-v1) — never hand-edit them. The two READMEs and authentication.md are hand-written. The authentication page documents the hsat.v1. token prefix, the settings/tokens flow, the show-once secret, and the fixed expiry options, all of which live in ld rather than in the spec. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/generate-hosted-api-v1.mjs | 51 + site/dolt/package.json | 3 +- .../src/content/products/hosted/api/README.md | 22 + .../content/products/hosted/api/v1/README.md | 101 ++ .../products/hosted/api/v1/authentication.md | 59 + .../products/hosted/api/v1/deployment.md | 362 ++++ .../content/products/hosted/api/v1/models.md | 279 +++ .../content/products/hosted/api/v1/user.md | 50 + site/dolt/src/nav.ts | 16 + specs/hosted-v1.yaml | 1509 +++++++++++++++++ 10 files changed, 2451 insertions(+), 1 deletion(-) create mode 100644 scripts/generate-hosted-api-v1.mjs create mode 100644 site/dolt/src/content/products/hosted/api/README.md create mode 100644 site/dolt/src/content/products/hosted/api/v1/README.md create mode 100644 site/dolt/src/content/products/hosted/api/v1/authentication.md create mode 100644 site/dolt/src/content/products/hosted/api/v1/deployment.md create mode 100644 site/dolt/src/content/products/hosted/api/v1/models.md create mode 100644 site/dolt/src/content/products/hosted/api/v1/user.md create mode 100644 specs/hosted-v1.yaml diff --git a/scripts/generate-hosted-api-v1.mjs b/scripts/generate-hosted-api-v1.mjs new file mode 100644 index 0000000..4fb913d --- /dev/null +++ b/scripts/generate-hosted-api-v1.mjs @@ -0,0 +1,51 @@ +#!/usr/bin/env node +/** + * Generates per-tag Markdown pages for the Hosted v1 API from the OpenAPI spec. + * + * Usage: + * node scripts/generate-hosted-api-v1.mjs + * + * Output: site/dolt/src/content/products/hosted/api/v1/ + * user.md — User-tagged endpoints + * deployment.md — Deployment-tagged endpoints + * models.md — all component schemas + * + * authentication.md and README.md are hand-written; this script does not touch + * them. + * + * The rendering lives in scripts/lib/openapi-docs.mjs, shared with the DoltHub + * v2 generator. + */ + +import { dirname, join } from "path"; +import { fileURLToPath } from "url"; +import { generateApiDocs } from "./lib/openapi-docs.mjs"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +generateApiDocs({ + specPath: join(__dirname, "../specs/hosted-v1.yaml"), + outDir: join(__dirname, "../site/dolt/src/content/products/hosted/api/v1"), + tagPages: [ + { + tag: "User", + file: "user.md", + frontmatter: + '---\ntitle: "User"\ndescription: The authenticated user resource in the Hosted v1 API.\n---\n\n# User', + }, + { + tag: "Deployment", + file: "deployment.md", + frontmatter: + '---\ntitle: "Deployment"\ndescription: Creating, listing, and reading Hosted Dolt deployments and their instances.\n---\n\n# Deployment', + }, + ], + models: { + file: "models.md", + href: "/products/hosted/api/v1/models", + frontmatter: + '---\ntitle: "Models"\ndescription: Request and response schemas for the Hosted v1 API.\n---\n\n# Models', + intro: + "Shared request and response types used across the v1 API. See the [error model](#model-problem) for how failures are reported.", + }, +}); diff --git a/site/dolt/package.json b/site/dolt/package.json index 3c25879..6ce6467 100644 --- a/site/dolt/package.json +++ b/site/dolt/package.json @@ -7,7 +7,8 @@ "dev": "astro dev --port 4321", "build": "node ../../scripts/check-content-frontmatter.mjs src/content && astro build && cp _redirects dist/_redirects && cp _headers dist/_headers && cp dist/docs/404.html dist/404.html && cp dist/docs/llms.txt dist/llms.txt && npx pagefind --site dist/docs", "preview": "astro preview --port 4321", - "generate-api-v2": "node ../../scripts/generate-api-v2.mjs" + "generate-api-v2": "node ../../scripts/generate-api-v2.mjs", + "generate-hosted-api-v1": "node ../../scripts/generate-hosted-api-v1.mjs" }, "dependencies": { "@astrojs/react": "^5.0.1", diff --git a/site/dolt/src/content/products/hosted/api/README.md b/site/dolt/src/content/products/hosted/api/README.md new file mode 100644 index 0000000..989f9ad --- /dev/null +++ b/site/dolt/src/content/products/hosted/api/README.md @@ -0,0 +1,22 @@ +--- +title: "Hosted Dolt API" +description: Programmatic access to your Hosted Dolt deployments. +--- + +# Hosted Dolt API + +Hosted Dolt exposes an HTTP API for managing deployments programmatically — creating them, listing them, and reading their configuration and state from scripts, CI, or your own tooling. + +## REST API + +- **[v1 API](/products/hosted/api/v1)** — the current generation. An explicit, versioned, OpenAPI-defined contract with consistent HTTP semantics, a single [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) error model, and a uniform response envelope. Start here. + +The conventions are deliberately the same as the [DoltHub v2 API](/products/dolthub/api/v2): the same error body, the same success envelope, and the same cursor pagination. If you already integrate with one, the other should feel familiar. + +## Scope + +The v1 API covers the **control plane** — the deployments themselves. Querying the data *inside* a deployment is not part of it: every deployment exposes a SQL endpoint that you connect to directly with your database credentials, exactly as you would any MySQL or Postgres server. See [Getting Started](/products/hosted/getting-started) for how to connect. + +Database credentials are never returned by this API. Reading a deployment tells you its configuration and state, never its secrets. + +> **Note:** please send requests to `https://hosted.doltdb.com`. diff --git a/site/dolt/src/content/products/hosted/api/v1/README.md b/site/dolt/src/content/products/hosted/api/v1/README.md new file mode 100644 index 0000000..69e1987 --- /dev/null +++ b/site/dolt/src/content/products/hosted/api/v1/README.md @@ -0,0 +1,101 @@ +--- +title: "Hosted API v1" +description: The Hosted Dolt v1 API — an explicit, versioned, OpenAPI-defined contract for deployments. +--- + +# Hosted API v1 + +_API version: v1_ + +The v1 API is the public HTTP surface for the Hosted Dolt control plane. Every endpoint lives under `https://hosted.doltdb.com/api/v1/`. + +It is an OpenAPI-defined contract, and commits to: + +- Consistent HTTP semantics (correct status codes, idempotent GETs, `202` for work that continues after the response) +- A single error model ([RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem details) — see [Problem](/products/hosted/api/v1/models#model-problem) +- A uniform success [Envelope](/products/hosted/api/v1/models#model-envelope) wrapping every response +- Cursor pagination on list endpoints + +**Scope.** v1 covers the control plane only. Querying the data inside a deployment is not part of this API — connect to the deployment's SQL endpoint directly with your database credentials. + +## Authentication + +Every endpoint requires a Hosted API token, sent as a bearer token: + +```sh +curl 'https://hosted.doltdb.com/api/v1/user' \ + -H 'Authorization: Bearer hsat.v1.YOUR_TOKEN_HERE' +``` + +See [Authentication](/products/hosted/api/v1/authentication) for how to create and manage tokens. + +## All endpoints + +### User + +| Method | Path | What it does | +|--------|------|--------------| +| **GET** | `/api/v1/user` | [Get the authenticated user](/products/hosted/api/v1/user#getCurrentUser) | + +### Deployment + +| Method | Path | What it does | +|--------|------|--------------| +| **GET** | `/api/v1/deployment-options` | [List the options a deployment can be created with](/products/hosted/api/v1/deployment#getDeploymentOptions) | +| **POST** | `/api/v1/deployments` | [Create a deployment](/products/hosted/api/v1/deployment#createDeployment) | +| **GET** | `/api/v1/deployments/{owner}` | [List an owner's deployments](/products/hosted/api/v1/deployment#listDeployments) | +| **GET** | `/api/v1/deployments/{owner}/{deployment}` | [Get a deployment](/products/hosted/api/v1/deployment#getDeployment) | +| **GET** | `/api/v1/deployments/{owner}/{deployment}/instances` | [List a deployment's instances](/products/hosted/api/v1/deployment#listDeploymentInstances) | + +## Response shape + +Every `2xx` response body is an [Envelope](/products/hosted/api/v1/models#model-envelope): the resource, or an array of resources, under `data`, with optional `meta`. + +```json +{ + "data": { "owner": "acme", "name": "analytics", "state": "started" } +} +``` + +List endpoints put the pagination cursor in `meta`: + +```json +{ + "data": [ { "owner": "acme", "name": "analytics" } ], + "meta": { "next_page_token": "eyJvZmZzZXQiOjI1fQ" } +} +``` + +When `meta.next_page_token` is present, pass it back as the `page_token` query parameter to fetch the next page. An absent or empty token means there are no further results. + +## Errors + +Every non-`2xx` response is a [Problem](/products/hosted/api/v1/models#model-problem) with content type `application/problem+json`: + +```json +{ + "type": "https://docs.dolthub.com/products/hosted/api/v1/models/#model-errorcode", + "title": "Not found", + "status": 404, + "detail": "Deployment 'analytics' does not exist for owner 'acme'", + "instance": "/api/v1/deployments/acme/analytics", + "code": "NOT_FOUND", + "request_id": "req_01HZX9P7Q5N2M8" +} +``` + +Branch on `code` — a stable, machine-readable [ErrorCode](/products/hosted/api/v1/models#model-errorcode) — never on the human-readable `title` or `detail`, which may be reworded at any time. + +Every response, including successful ones, carries an `x-request-id` header, echoed in the body as `request_id` on errors. Include it when contacting support so a request can be traced end to end. + +## Long-running work + +Creating a deployment returns `202 Accepted` with the deployment in its `starting` state — provisioning continues after the response. Poll [Get a deployment](/products/hosted/api/v1/deployment#getDeployment) until `state` becomes `started`. + +Deployment names are unique within an owner, which makes creates idempotent by name: retrying after an ambiguous failure returns `409 Conflict` rather than provisioning a second deployment. + +> **Creating a deployment incurs cost.** + +## Stability + +v1 is additive. New endpoints, new optional request fields, new response fields, and new `ErrorCode` values may be introduced within v1. Renaming or removing a field, or changing an existing one's meaning, requires a new major version. diff --git a/site/dolt/src/content/products/hosted/api/v1/authentication.md b/site/dolt/src/content/products/hosted/api/v1/authentication.md new file mode 100644 index 0000000..f2650cd --- /dev/null +++ b/site/dolt/src/content/products/hosted/api/v1/authentication.md @@ -0,0 +1,59 @@ +--- +title: "Authentication" +description: How to authenticate requests to the Hosted Dolt v1 API. +--- + +# Authentication + +_API version: v1_ + +Every v1 endpoint requires authentication. The only credential type is a **Hosted API token**, sent in the `Authorization` header as a bearer token. + +## Creating a token + +Create a token from the **Tokens** section of your account settings at [hosted.doltdb.com/settings/tokens](https://hosted.doltdb.com/settings/tokens). Give it a name that says where it will be used — `deploy pipeline`, `staging bootstrap` — since that name is how you will identify it later. + +Copy the token immediately. Only a hash of it is stored, so the secret is shown once and cannot be recovered afterwards. If you lose it, delete the token and create another. + +Tokens are prefixed `hsat.v1.`. + +## Using a token + +Send it as a bearer token on every request: + +```sh +curl 'https://hosted.doltdb.com/api/v1/user' \ + -H 'Authorization: Bearer hsat.v1.YOUR_TOKEN_HERE' +``` + +`GET /api/v1/user` is the cheapest way to check a credential: a `200` confirms the token is valid and shows whose access it carries. + +In a script, keep the token in an environment variable rather than in the source: + +```sh +export HOSTED_API_TOKEN='hsat.v1.YOUR_TOKEN_HERE' + +curl 'https://hosted.doltdb.com/api/v1/deployments/acme' \ + -H "Authorization: Bearer $HOSTED_API_TOKEN" +``` + +## Permissions + +A token carries the full permissions of the user who created it. There are no per-token scopes in v1 — anything you can do in the web UI, a token of yours can do through the API, including [creating deployments, which incurs cost](/products/hosted/api/v1/deployment#createDeployment). + +Access to a particular deployment still follows that deployment's own roles. A deployment you cannot see returns `404` rather than `403`, so the API never reveals that a deployment exists to someone without access to it. + +## Expiry and revocation + +Every token has an expiry date, chosen when you create it — 30, 60, or 90 days, or one year. There is deliberately no non-expiring option; rotate long-lived automation on a schedule that matches the expiry you pick. + +Delete a token from the same settings page to revoke it immediately. The tokens table also shows when each token was last used, and from where, which is the fastest way to find out whether a token is still in service before you delete it. + +## Failure responses + +| Status | Code | Meaning | +|--------|------|---------| +| `401` | `UNAUTHENTICATED` | The `Authorization` header was missing, malformed, or the token is invalid or expired. | +| `403` | `PERMISSION_DENIED` | The token is valid, but its user is not permitted to perform this action. | + +Both are returned as [Problem](/products/hosted/api/v1/models#model-problem) documents. See the [v1 overview](/products/hosted/api/v1#errors) for the error model. diff --git a/site/dolt/src/content/products/hosted/api/v1/deployment.md b/site/dolt/src/content/products/hosted/api/v1/deployment.md new file mode 100644 index 0000000..21bfa61 --- /dev/null +++ b/site/dolt/src/content/products/hosted/api/v1/deployment.md @@ -0,0 +1,362 @@ +--- +title: "Deployment" +description: Creating, listing, and reading Hosted Dolt deployments and their instances. +--- + +# Deployment + +Hosted Dolt deployments and their lifecycle. + +## List the options a deployment can be created with {#getDeploymentOptions} +GET /api/v1/deployment-options + +Returns the zones, instance types, and storage options available for a cloud, so a caller can construct a valid `POST /api/v1/deployments` request. + +The options narrow in steps, because each depends on the one before it. Supply `cloud` alone for its zones; add `zone` to also get that zone's instance types; add `instance_type_id` to also get the storage options compatible with that instance. Fields you haven't narrowed enough to determine are absent rather than empty. + +Each list is filtered to what you selected: supplying `zone` narrows `zones` to that zone, and supplying `instance_type_id` narrows `instance_types` to that instance type. A fully narrowed request therefore describes one combination rather than repeating the whole catalogue. + +A `zone` or `instance_type_id` that doesn't exist is a `422` rather than an empty result, so a typo can't be mistaken for a combination with nothing available. `instance_type_id` requires `zone`; supplying it alone is a `400`. + +The `id` of an instance type or storage option is what `POST /api/v1/deployments` accepts; `name` is for display. + + +**Parameters** + +| Name | In | Type | Required | Description | +|------|----|------|----------|-------------| +| `cloud` | query | string | yes | The cloud to list options for. | +| `zone` | query | string | no | A zone from this cloud's `zones`. Supply it to receive `instance_types`. | +| `instance_type_id` | query | string | no | An instance type `id` from `instance_types`. Supply it, together with `zone`, to receive `storage_options`. | + +**Example request** + +```sh +curl -X GET 'https://hosted.doltdb.com/api/v1/deployment-options?cloud=aws' \ + -H 'Authorization: Bearer YOUR_TOKEN' +``` + +**Responses** + +| Status | Description | Schema | +|--------|-------------|--------| +| `200` | The available options, narrowed by the supplied parameters. | [`DeploymentOptions`](/products/hosted/api/v1/models#model-deploymentoptions) | +| `400` | The request was malformed or failed input validation. | | +| `401` | Authentication credentials were missing or invalid. | | +| `405` | The HTTP method is not supported for this resource. | | +| `422` | The request was well-formed but semantically invalid. | | +| `500` | An unexpected server error occurred. | | +| `503` | The service is temporarily unavailable. | | + +**Example response `200`** + +```json +{ + "data": { + "cloud": "aws", + "zones": [ + "us-east-1" + ], + "instance_types": [ + { + "id": "aws.t2.medium", + "name": "t2.medium", + "cpus": 2, + "memory_gb": 4, + "description": "Trial tier, the lowest spec that runs a Dolt SQL server.", + "hourly_cost_usd": 0.06849315 + } + ], + "storage_options": [ + { + "id": "aws.ebs.gp3_50", + "name": "Trial 50GB EBS", + "description": "Trial tier storage capped at 50GB", + "min_size_gb": 50, + "max_size_gb": 50, + "monthly_cost_usd_per_gb": 0 + } + ] + } +} +``` + +--- + +## Create a deployment {#createDeployment} +POST /api/v1/deployments + +Provisions a new deployment and returns `202` with the deployment in its `starting` state. Provisioning continues after the response: poll `GET /api/v1/deployments/{owner}/{deployment}` until `state` becomes `started`. + +Deployment names are unique within an owner, so a create is idempotent by name — a retry after an ambiguous failure returns `409` rather than provisioning a second deployment. Callers should still treat `409` as "it already exists", not as a different failure. + +`instance_type_id` and `volume_type_id` take the **ids** from the deployment options endpoint, not the display names a deployment reports back on a read. + +A `5xx` from this endpoint does not guarantee the deployment was *not* created: the response body is read back after provisioning is accepted, so a failure in that read surfaces as an error for a create that succeeded. Retrying is the correct response — it returns `409` if the deployment now exists, and `GET` confirms either way. + +**Creating a deployment incurs cost.** + + +**Request body** + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `owner` | string | yes | The user or organization that will own the deployment. The caller must have permission to create deployments for it. 3–32 characters of letters, digits, hyphens, and underscores. | +| `name` | string | yes | The deployment name, unique within the owner. 3–32 characters of letters, digits, hyphens, and underscores. | +| `cloud` | string | yes | The cloud the deployment runs in. | +| `zone` | string | yes | The cloud region to provision in, as listed by the deployment options. | +| `cluster_type` | string | no | The database engine the deployment runs. `mysql_with_dolt_replicas` is a MySQL primary with Dolt read replicas. | +| `instance_type_id` | string | yes | The **id** of the instance type, from the deployment options endpoint. Note a deployment reports `instance_type_name` on a read — the id and the display name are different values. | +| `volume_type_id` | string | yes | The **id** of the storage type, from the deployment options endpoint. As with `instance_type_id`, this is the id rather than the display name. | +| `volume_size_gb` | integer | yes | The size of the storage volume, in gigabytes. Must fall within the selected storage type's supported range. | +| `replicas` | integer | no | The number of read replicas. Defaults to `0` when omitted. | +| `webpki_cert` | boolean | no | Serve a publicly-trusted (WebPKI) TLS certificate rather than a Hosted-issued one. Defaults to `false` when omitted. | +| `expose_remotesapi_endpoint` | boolean | no | Expose a Dolt remotes API endpoint. Defaults to `false` when omitted. | +| `expose_mcp` | boolean | no | Expose an MCP endpoint. Defaults to `false` when omitted. | +| `expose_stats` | boolean | no | Expose a statistics endpoint. Defaults to `false` when omitted. | + +**Example request** + +```sh +curl -X POST 'https://hosted.doltdb.com/api/v1/deployments' \ + -H 'Authorization: Bearer YOUR_TOKEN' \ + -H 'Content-Type: application/json' \ + -d '{"owner":"acme","name":"analytics","cloud":"aws","zone":"us-east-1","instance_type_id":"aws.t2.medium","volume_type_id":"aws.ebs.gp3_50","volume_size_gb":50}' +``` + +**Responses** + +| Status | Description | Schema | +|--------|-------------|--------| +| `202` | The deployment has been accepted and is provisioning. `state` is `starting`. | [`Deployment`](/products/hosted/api/v1/models#model-deployment) | +| `400` | The request was malformed or failed input validation. | | +| `401` | Authentication credentials were missing or invalid. | | +| `403` | Authenticated, but not permitted to perform this action. | | +| `405` | The HTTP method is not supported for this resource. | | +| `409` | The request conflicts with the current state of the resource (e.g. it already exists). | | +| `422` | The request was well-formed but semantically invalid. | | +| `500` | An unexpected server error occurred. | | +| `503` | The service is temporarily unavailable. | | + +**Example response `202`** + +```json +{ + "data": { + "owner": "acme", + "name": "analytics", + "state": "starting", + "cloud": "aws", + "zone": "us-east-1", + "cluster_type": "dolt", + "instance_type_name": "t2.medium", + "volume_type_name": "Trial 50GB EBS", + "volume_size_gb": 50, + "replicas": 0, + "host": "", + "port": 3306, + "caller_role": "admin", + "created_by": "acme-ops", + "created_at": "2026-08-11T09:14:00Z" + } +} +``` + +--- + +## List an owner's deployments {#listDeployments} +GET /api/v1/deployments/{owner} + +Returns the deployments belonging to `{owner}` that the caller can see, newest cursor page first. Requires a credential with access to the owner. + +Items are `DeploymentSummary`, not the full `Deployment` — the backing RPC returns a narrower shape for lists. Fetch `GET /api/v1/deployments/{owner}/{deployment}` for the complete resource. + +Pagination is cursor-based: when `meta.next_page_token` is present, pass it back as `page_token` to fetch the next page. An absent or empty token means there are no further results. + + +**Parameters** + +| Name | In | Type | Required | Description | +|------|----|------|----------|-------------| +| `owner` | path | string | yes | The user or organization whose deployments to list. 3–32 characters of letters, digits, hyphens, and underscores. | +| `page_token` | query | string | no | The `meta.next_page_token` from a previous response. Omit for the first page. | +| `state` | query | string | no | Return only deployments in this state. Omit for all states. | + +**Example request** + +```sh +curl -X GET 'https://hosted.doltdb.com/api/v1/deployments/{owner}' \ + -H 'Authorization: Bearer YOUR_TOKEN' +``` + +**Responses** + +| Status | Description | Schema | +|--------|-------------|--------| +| `200` | The owner's deployments. | [`DeploymentSummary[]`](/products/hosted/api/v1/models#model-deploymentsummary) | +| `400` | The request was malformed or failed input validation. | | +| `401` | Authentication credentials were missing or invalid. | | +| `403` | Authenticated, but not permitted to perform this action. | | +| `404` | The requested resource does not exist. | | +| `405` | The HTTP method is not supported for this resource. | | +| `500` | An unexpected server error occurred. | | + +**Example response `200`** + +```json +{ + "data": [ + { + "owner": "acme", + "name": "analytics", + "state": "started", + "cloud": "aws", + "zone": "us-west-2", + "cluster_type": "dolt", + "instance_type_name": "m5.large", + "volume_type_name": "gp3", + "volume_size_gb": 100, + "replicas": 0, + "database_version": "1.58.4", + "hourly_cost_usd": 0.192, + "webpki_cert": true + } + ], + "meta": { + "next_page_token": "eyJvZmZzZXQiOjI1fQ" + } +} +``` + +--- + +## Get a deployment {#getDeployment} +GET /api/v1/deployments/{owner}/{deployment} + +Returns the deployment `{owner}/{deployment}`. Requires a credential with at least read access; a deployment the caller cannot see returns `404` rather than `403`, so its existence isn't leaked. + + +**Parameters** + +| Name | In | Type | Required | Description | +|------|----|------|----------|-------------| +| `owner` | path | string | yes | The user or organization that owns the deployment. 3–32 characters of letters, digits, hyphens, and underscores. | +| `deployment` | path | string | yes | The deployment name, unique within the owner. 3–32 characters of letters, digits, hyphens, and underscores. | + +**Example request** + +```sh +curl -X GET 'https://hosted.doltdb.com/api/v1/deployments/{owner}/{deployment}' \ + -H 'Authorization: Bearer YOUR_TOKEN' +``` + +**Responses** + +| Status | Description | Schema | +|--------|-------------|--------| +| `200` | The deployment. | [`Deployment`](/products/hosted/api/v1/models#model-deployment) | +| `400` | The request was malformed or failed input validation. | | +| `401` | Authentication credentials were missing or invalid. | | +| `404` | The requested resource does not exist. | | +| `405` | The HTTP method is not supported for this resource. | | +| `500` | An unexpected server error occurred. | | +| `503` | The service is temporarily unavailable. | | + +**Example response `200`** + +```json +{ + "data": { + "owner": "acme", + "name": "analytics", + "state": "started", + "cloud": "aws", + "zone": "us-east-1", + "cluster_type": "dolt", + "instance_type_name": "t2.medium", + "volume_type_name": "Trial 50GB EBS", + "volume_size_gb": 50, + "replicas": 0, + "database_version": "1.58.4", + "host": "analytics.dbs.hosted.doltdb.com", + "port": 3306, + "hourly_cost_usd": 0.06849315, + "webpki_cert": true, + "expose_remotesapi_endpoint": false, + "expose_mcp": false, + "expose_stats": false, + "disable_automatic_dolt_updates": false, + "caller_role": "admin", + "created_by": "acme-ops", + "created_at": "2026-07-01T18:22:04Z" + } +} +``` + +--- + +## List a deployment's instances {#listDeploymentInstances} +GET /api/v1/deployments/{owner}/{deployment}/instances + +Returns the instances backing `{owner}/{deployment}` — one for a single-instance deployment, or a primary plus its read replicas. + +Stopped instances are not listed; starting, started, and stopping ones all are. So an instance appearing here is part of the deployment but not necessarily serving traffic, and an empty array means it has none outside the stopped state — normal while a deployment is itself `starting`. + +The list is not paginated: a deployment has a primary and its replicas, a set small enough to return whole. + + +**Parameters** + +| Name | In | Type | Required | Description | +|------|----|------|----------|-------------| +| `owner` | path | string | yes | The user or organization that owns the deployment. 3–32 characters of letters, digits, hyphens, and underscores. | +| `deployment` | path | string | yes | The deployment name, unique within the owner. 3–32 characters of letters, digits, hyphens, and underscores. | + +**Example request** + +```sh +curl -X GET 'https://hosted.doltdb.com/api/v1/deployments/{owner}/{deployment}/instances' \ + -H 'Authorization: Bearer YOUR_TOKEN' +``` + +**Responses** + +| Status | Description | Schema | +|--------|-------------|--------| +| `200` | The deployment's non-stopped instances. | [`DeploymentInstance[]`](/products/hosted/api/v1/models#model-deploymentinstance) | +| `400` | The request was malformed or failed input validation. | | +| `401` | Authentication credentials were missing or invalid. | | +| `404` | The requested resource does not exist. | | +| `405` | The HTTP method is not supported for this resource. | | +| `500` | An unexpected server error occurred. | | +| `503` | The service is temporarily unavailable. | | + +**Example response `200`** + +```json +{ + "data": [ + { + "id": "9b1f5c2e-4d3a-4f8b-9c0d-1e2f3a4b5c6d", + "index": 0, + "is_primary": true, + "host": "analytics-0.dbs.hosted.doltdb.com", + "instance_type_name": "t2.medium", + "volume_type_name": "Trial 50GB EBS", + "volume_size_gb": 50, + "hourly_cost_usd": 0.06849315 + }, + { + "id": "2c4e6a8b-1d3f-4a5c-8e9b-0f1a2b3c4d5e", + "index": 1, + "is_primary": false, + "host": "analytics-1.dbs.hosted.doltdb.com", + "instance_type_name": "t2.medium", + "volume_type_name": "Trial 50GB EBS", + "volume_size_gb": 50, + "hourly_cost_usd": 0.06849315 + } + ] +} +``` + diff --git a/site/dolt/src/content/products/hosted/api/v1/models.md b/site/dolt/src/content/products/hosted/api/v1/models.md new file mode 100644 index 0000000..fc32931 --- /dev/null +++ b/site/dolt/src/content/products/hosted/api/v1/models.md @@ -0,0 +1,279 @@ +--- +title: "Models" +description: Request and response schemas for the Hosted v1 API. +--- + +# Models + +Shared request and response types used across the v1 API. See the [error model](#model-problem) for how failures are reported. + +## ErrorCode {#model-errorcode} +A stable, machine-readable error code in SCREAMING_SNAKE_CASE. Clients branch on this value, never on the human-readable `title`/`detail` prose. The baseline codes below cover the standard HTTP failure categories; endpoint-specific codes (e.g. `DEPLOYMENT_NOT_FOUND`) are appended to this enum alongside the endpoints that emit them, which is an additive, non-breaking change under the v1 stability policy. + +**Enum values** + +| Value | +|-------| +| `VALIDATION_FAILED` | +| `UNAUTHENTICATED` | +| `PERMISSION_DENIED` | +| `NOT_FOUND` | +| `METHOD_NOT_ALLOWED` | +| `CONFLICT` | +| `UNPROCESSABLE` | +| `RATE_LIMITED` | +| `INTERNAL` | +| `SERVICE_UNAVAILABLE` | +| `OPERATION_FAILED` | + +--- + +## Problem {#model-problem} +A structured error body returned for every non-2xx response, following RFC 9457 (Problem Details for HTTP APIs). This is the single error model for the entire v1 API — there are no ad-hoc error shapes. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `string` | yes | A URI identifying the problem type; when dereferenced it points at human-readable documentation for the error. | +| `title` | `string` | yes | A short, human-readable summary of the problem type. | +| `status` | `integer` | yes | The HTTP status code, repeated in the body for convenience. | +| `detail` | `string` | no | A human-readable explanation specific to this occurrence of the problem. | +| `instance` | `string` | no | A URI reference identifying the specific occurrence (typically the request path). | +| `code` | `string` | yes | A stable, machine-readable error code in SCREAMING_SNAKE_CASE. Clients branch on this value, never on the human-readable `title`/`detail` prose. The baseline codes below cover the standard HTTP failure categories; endpoint-specific codes (e.g. `DEPLOYMENT_NOT_FOUND`) are appended to this enum alongside the endpoints that emit them, which is an additive, non-breaking change under the v1 stability policy. | +| `request_id` | `string` | yes | The request identifier, echoed on every response. Include it when contacting support so a request can be traced end-to-end. | + +--- + +## Meta {#model-meta} +Response metadata carried alongside the primary `data` payload. All fields are optional; list endpoints populate `next_page_token` for cursor pagination. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `next_page_token` | `string` | no | Opaque cursor for the next page of a list response. Absent or empty when there are no further results; otherwise pass it back as the `page_token` query parameter to fetch the next page. | + +--- + +## Envelope {#model-envelope} +The success envelope wrapping every 2xx response body: the resource or list of resources under `data`, with optional `meta`. This is the single success shape for the API — there are no unenveloped success bodies. Endpoints narrow `data` to a concrete resource via `allOf`; the base leaves `data` unconstrained so that composition works. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `data` | `object,array` | yes | The primary response payload — a resource, or an array of resources for list endpoints. | +| `meta` | `object` | no | Response metadata carried alongside the primary `data` payload. All fields are optional; list endpoints populate `next_page_token` for cursor pagination. | + +--- + +## UserEmailAddress {#model-useremailaddress} +An email address belonging to a user. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `address` | `string` | yes | The email address. | +| `is_verified` | `boolean` | yes | Whether the address has completed email verification. | +| `is_primary` | `boolean` | yes | Whether this is the user's primary address. Exactly one address is primary. | + +--- + +## User {#model-user} +A Hosted user. `GET /api/v1/user` returns the authenticated user's profile. v1 returns only public-facing profile fields — the identity provider the account signs in with, session state, and credential metadata are never included. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `username` | `string` | yes | The user's Hosted username (unique handle). | +| `display_name` | `string` | no | The user's display name. May be empty. | +| `company` | `string` | no | The user's stated company. May be empty. | +| `email_addresses` | `array` | yes | The user's email addresses. Returned only for the authenticated user themselves; empty for any other caller. | + +--- + +## InstanceType {#model-instancetype} +A compute instance type a deployment can run on. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `id` | `string` | yes | The identifier `POST /api/v1/deployments` accepts as `instance_type_id`. | +| `name` | `string` | yes | The display name. A deployment reports this as `instance_type_name`. | +| `cpus` | `integer` | no | Virtual CPUs. | +| `memory_gb` | `integer` | no | Memory, in gigabytes. | +| `description` | `string` | no | A human-readable summary of what the instance suits. | +| `hourly_cost_usd` | `number` | no | Cost per hour in US dollars. Absent when no hourly price is published for this instance type. | + +--- + +## StorageOption {#model-storageoption} +A storage type a deployment's volume can use. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `id` | `string` | yes | The identifier `POST /api/v1/deployments` accepts as `volume_type_id`. | +| `name` | `string` | yes | The display name. A deployment reports this as `volume_type_name`. | +| `description` | `string` | no | A human-readable summary of the storage type. | +| `min_size_gb` | `integer` | no | Smallest volume size this type supports, in gigabytes. `volume_size_gb` on a create request must be at least this. | +| `max_size_gb` | `integer` | no | Largest volume size this type supports, in gigabytes. | +| `monthly_cost_usd_per_gb` | `number` | no | Cost per gigabyte per month, in US dollars. | + +--- + +## DeploymentInstance {#model-deploymentinstance} +One instance backing a deployment. A deployment has a primary and, when it has read replicas, one instance per replica. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `id` | `string` | yes | The instance's identifier, unique within the deployment. | +| `index` | `integer` | yes | The instance's position in the deployment. `0` is the first instance; replicas take the following indices. | +| `is_primary` | `boolean` | yes | Whether this instance is currently the primary. Exactly one instance of a started deployment is primary, and which one can change over the deployment's life. | +| `host` | `string` | no | The hostname for this specific instance. Connect to the deployment's own `host` unless you mean to address one instance directly. | +| `instance_type_name` | `string` | no | The display name of this instance's type. | +| `volume_type_name` | `string` | no | The display name of this instance's storage type. | +| `volume_size_gb` | `integer` | no | The size of this instance's storage volume, in gigabytes. | +| `hourly_cost_usd` | `number` | no | This instance's cost per hour, in US dollars. A deployment's total is the sum across its instances. | + +--- + +## DeploymentOptions {#model-deploymentoptions} +The options available for creating a deployment, narrowed by the query parameters supplied. `instance_types` and `storage_options` are absent until enough of the chain has been supplied to determine them. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `cloud` | `string` | yes | The cloud the deployment runs in. | +| `zones` | `array` | yes | The zones this cloud supports. | +| `instance_types` | `array` | no | Instance types available in the requested `zone`. Absent when `zone` wasn't supplied. | +| `storage_options` | `array` | no | Storage types compatible with the requested `instance_type_id`. Absent when `zone` and `instance_type_id` weren't both supplied. | + +--- + +## CreateDeploymentRequest {#model-createdeploymentrequest} +The provisioning parameters for a new deployment. + +v1.0 exposes the core parameters only. Restoring from a backup, cloning an existing deployment, workbench user settings, and private networking are all settable on the internal API but are not part of this request; each is its own feature with its own contract, and adding them is additive. The internal deployment test flag is deliberately not exposed at all. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `owner` | `string` | yes | The user or organization that will own the deployment. The caller must have permission to create deployments for it. 3–32 characters of letters, digits, hyphens, and underscores. | +| `name` | `string` | yes | The deployment name, unique within the owner. 3–32 characters of letters, digits, hyphens, and underscores. | +| `cloud` | `string` | yes | The cloud the deployment runs in. | +| `zone` | `string` | yes | The cloud region to provision in, as listed by the deployment options. | +| `cluster_type` | `string` | no | The database engine the deployment runs. `mysql_with_dolt_replicas` is a MySQL primary with Dolt read replicas. | +| `instance_type_id` | `string` | yes | The **id** of the instance type, from the deployment options endpoint. Note a deployment reports `instance_type_name` on a read — the id and the display name are different values. | +| `volume_type_id` | `string` | yes | The **id** of the storage type, from the deployment options endpoint. As with `instance_type_id`, this is the id rather than the display name. | +| `volume_size_gb` | `integer` | yes | The size of the storage volume, in gigabytes. Must fall within the selected storage type's supported range. | +| `replicas` | `integer` | no | The number of read replicas. Defaults to `0` when omitted. | +| `webpki_cert` | `boolean` | no | Serve a publicly-trusted (WebPKI) TLS certificate rather than a Hosted-issued one. Defaults to `false` when omitted. | +| `expose_remotesapi_endpoint` | `boolean` | no | Expose a Dolt remotes API endpoint. Defaults to `false` when omitted. | +| `expose_mcp` | `boolean` | no | Expose an MCP endpoint. Defaults to `false` when omitted. | +| `expose_stats` | `boolean` | no | Expose a statistics endpoint. Defaults to `false` when omitted. | + +--- + +## DeploymentState {#model-deploymentstate} +The deployment's lifecycle state. `starting` covers both initial provisioning and a restart; poll this field to observe a create or a resize reaching `started`. + +**Enum values** + +| Value | +|-------| +| `starting` | +| `started` | +| `stopping` | +| `stopped` | + +--- + +## CloudProvider {#model-cloudprovider} +The cloud the deployment runs in. + +**Enum values** + +| Value | +|-------| +| `aws` | +| `gcp` | +| `azure` | + +--- + +## ClusterType {#model-clustertype} +The database engine the deployment runs. `mysql_with_dolt_replicas` is a MySQL primary with Dolt read replicas. + +**Enum values** + +| Value | +|-------| +| `dolt` | +| `doltgres` | +| `mysql_with_dolt_replicas` | + +--- + +## DeploymentRole {#model-deploymentrole} +The authenticated caller's role on this deployment. Always present on a read, since a caller without at least read access cannot retrieve the deployment at all. + +**Enum values** + +| Value | +|-------| +| `admin` | +| `writer` | +| `reader` | +| `reader_and_pulls` | + +--- + +## DeploymentSummary {#model-deploymentsummary} +A deployment as it appears in a list. + +This is deliberately not the same shape as `Deployment`. The list RPC returns a narrower record — it omits connection details, the caller's role, and the creation audit fields, and it adds the last-backup figures shown in fleet views. Read the deployment itself for the full resource. As with `Deployment`, database credentials are never included. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `owner` | `string` | yes | The user or organization that owns the deployment. | +| `name` | `string` | yes | The deployment name, unique within the owner. | +| `state` | `string` | yes | The deployment's lifecycle state. `starting` covers both initial provisioning and a restart; poll this field to observe a create or a resize reaching `started`. | +| `cloud` | `string` | yes | The cloud the deployment runs in. | +| `zone` | `string` | yes | The cloud region the deployment runs in. | +| `cluster_type` | `string` | yes | The database engine the deployment runs. `mysql_with_dolt_replicas` is a MySQL primary with Dolt read replicas. | +| `instance_type_name` | `string` | no | The display name of the deployment's instance type. | +| `volume_type_name` | `string` | no | The display name of the deployment's storage type. | +| `volume_size_gb` | `integer` | no | The size of the deployment's storage volume, in gigabytes. | +| `replicas` | `integer` | no | The number of read replicas. `0` for a single-instance deployment. | +| `database_version` | `string` | no | The version of the database engine the deployment is running — a Dolt version for a `dolt` cluster, a Doltgres version for a `doltgres` one. | +| `hourly_cost_usd` | `number` | no | The deployment's current cost per hour, in US dollars. | +| `webpki_cert` | `boolean` | no | Whether the deployment serves a publicly-trusted (WebPKI) TLS certificate. | +| `last_backup_size_bytes` | `integer` | no | Size of the most recent backup, in bytes. Absent when no backup has been taken or its size has not been computed yet. | +| `last_backup_time` | `string` | no | When the most recent backup was taken. Absent when no backup has been taken. | + +--- + +## Deployment {#model-deployment} +A Hosted Dolt deployment. + +v1 returns configuration and lifecycle state only. The deployment's database credentials are deliberately **not** part of this resource — they are issued and rotated through the deployment credentials endpoints, so that reading a deployment is never a credential-disclosing operation. + +Provider-specific private-networking configuration (AWS PrivateLink, GCP Private Service Connect, Azure Private Link) is not included in v1.0. Each carries its own endpoint collection and provisioning state, and will land as its own sub-resource; adding it is additive under the stability policy. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `owner` | `string` | yes | The user or organization that owns the deployment. | +| `name` | `string` | yes | The deployment name, unique within the owner. | +| `state` | `string` | yes | The deployment's lifecycle state. `starting` covers both initial provisioning and a restart; poll this field to observe a create or a resize reaching `started`. | +| `cloud` | `string` | yes | The cloud the deployment runs in. | +| `zone` | `string` | yes | The cloud region the deployment runs in. | +| `cluster_type` | `string` | yes | The database engine the deployment runs. `mysql_with_dolt_replicas` is a MySQL primary with Dolt read replicas. | +| `instance_type_name` | `string` | no | The display name of the deployment's instance type. Note this is the *name*, not the id that `POST /api/v1/deployments` accepts; both are listed by the deployment options endpoint. | +| `volume_type_name` | `string` | no | The display name of the deployment's storage type. As with `instance_type_name`, this is the name rather than the id used to create a deployment. | +| `volume_size_gb` | `integer` | no | The size of the deployment's storage volume, in gigabytes. | +| `replicas` | `integer` | no | The number of read replicas. `0` for a single-instance deployment. | +| `database_version` | `string` | no | The version of the database engine the deployment is running — a Dolt version for a `dolt` cluster, a Doltgres version for a `doltgres` one. | +| `host` | `string` | no | The hostname clients connect to. Empty until the deployment reaches `started`. | +| `port` | `integer` | no | The port clients connect to. | +| `hourly_cost_usd` | `number` | no | The deployment's current cost per hour, in US dollars. | +| `webpki_cert` | `boolean` | no | Whether the deployment serves a publicly-trusted (WebPKI) TLS certificate rather than a Hosted-issued one. | +| `expose_remotesapi_endpoint` | `boolean` | no | Whether the deployment exposes a Dolt remotes API endpoint. | +| `expose_mcp` | `boolean` | no | Whether the deployment exposes an MCP endpoint. | +| `expose_stats` | `boolean` | no | Whether the deployment exposes a statistics endpoint. | +| `disable_automatic_dolt_updates` | `boolean` | no | Whether automatic Dolt version updates are disabled for this deployment. | +| `caller_role` | `string` | yes | The authenticated caller's role on this deployment. Always present on a read, since a caller without at least read access cannot retrieve the deployment at all. | +| `created_by` | `string` | no | The username of the user who created the deployment. | +| `created_at` | `string` | yes | When the deployment was created. | +| `destroy_at` | `string` | no | When the deployment is scheduled to be destroyed. Absent unless a destroy has been scheduled. | +| `destroyed_by` | `string` | no | The username of the user who destroyed the deployment. Absent unless it has been destroyed. | + diff --git a/site/dolt/src/content/products/hosted/api/v1/user.md b/site/dolt/src/content/products/hosted/api/v1/user.md new file mode 100644 index 0000000..916cfe5 --- /dev/null +++ b/site/dolt/src/content/products/hosted/api/v1/user.md @@ -0,0 +1,50 @@ +--- +title: "User" +description: The authenticated user resource in the Hosted v1 API. +--- + +# User + +The authenticated user. + +## Get the authenticated user {#getCurrentUser} +GET /api/v1/user + +Returns the profile of the user identified by the request's credentials. Useful as a credential check: a `200` confirms the token is valid and shows whose access it carries. + + +**Example request** + +```sh +curl -X GET 'https://hosted.doltdb.com/api/v1/user' \ + -H 'Authorization: Bearer YOUR_TOKEN' +``` + +**Responses** + +| Status | Description | Schema | +|--------|-------------|--------| +| `200` | The authenticated user's profile. | [`User`](/products/hosted/api/v1/models#model-user) | +| `401` | Authentication credentials were missing or invalid. | | +| `405` | The HTTP method is not supported for this resource. | | +| `500` | An unexpected server error occurred. | | + +**Example response `200`** + +```json +{ + "data": { + "username": "acme-ops", + "display_name": "Acme Operations", + "company": "Acme Corp", + "email_addresses": [ + { + "address": "ops@acme.com", + "is_verified": true, + "is_primary": true + } + ] + } +} +``` + diff --git a/site/dolt/src/nav.ts b/site/dolt/src/nav.ts index ab77403..cd3d1af 100644 --- a/site/dolt/src/nav.ts +++ b/site/dolt/src/nav.ts @@ -244,6 +244,22 @@ const nav: NavSection[] = [ { title: "Infrastructure", href: "/products/hosted/infrastructure" }, { title: "Private Networking", href: "/products/hosted/private-networking" }, { title: "SSO", href: "/products/hosted/sso" }, + { + title: "API", + href: "/products/hosted/api", + children: [ + { + title: "v1", + href: "/products/hosted/api/v1", + children: [ + { title: "Authentication", href: "/products/hosted/api/v1/authentication" }, + { title: "User", href: "/products/hosted/api/v1/user" }, + { title: "Deployment", href: "/products/hosted/api/v1/deployment" }, + { title: "Models", href: "/products/hosted/api/v1/models" }, + ], + }, + ], + }, ], }, { diff --git a/specs/hosted-v1.yaml b/specs/hosted-v1.yaml new file mode 100644 index 0000000..6f05f9e --- /dev/null +++ b/specs/hosted-v1.yaml @@ -0,0 +1,1509 @@ +# Hosted Public API v1 — OpenAPI contract. +# +# This is the single source of truth for the v1 API. TypeScript DTO types, +# runtime/contract-test JSON Schemas, and the public docs are all generated +# from this file — never hand-edited downstream. +# +# The error model, envelope, and pagination conventions below are deliberately +# identical to DoltHub's v2 spec (web/packages/dolthub/openapi/v2.yaml) apart +# from the docs URL. Two public APIs from the same company that disagree about +# error bodies is a worse outcome than either individual choice. +openapi: 3.1.0 + +info: + title: Hosted API + version: 1.0.0 + summary: The public REST API for Hosted Dolt deployments. + description: | + The Hosted v1 API is an explicit, versioned, OpenAPI-defined contract for the Hosted + control plane: deployments and their instances, backups, credentials, configuration, + and access. + + This document is the contract: it is the source of truth for the implementation, the + generated types, and the rendered reference docs. + + **Scope.** v1 covers the control plane only. Querying the data inside a deployment is + not part of this API — every deployment exposes a SQL endpoint that callers connect to + directly with their own credentials. + contact: + name: DoltHub Support + url: https://hosted.doltdb.com/contact + email: dolt-interest@dolthub.com + license: + name: Hosted Dolt Terms of Service + url: https://hosted.doltdb.com/terms + termsOfService: https://hosted.doltdb.com/terms + +servers: + - url: https://hosted.doltdb.com + description: Production + +# Default: every operation requires an API token. Individual operations may override this +# once paths are added. API tokens are the only credential in v1 — the scheme list is a +# sequence so an `oauth2` scheme can be added later without a breaking change. +security: + - apiToken: [] + +# Operation tags are registered here so renderers group operations consistently. +tags: + - name: User + description: The authenticated user. + - name: Deployment + description: Hosted Dolt deployments and their lifecycle. + +paths: + /api/v1/user: + get: + operationId: getCurrentUser + summary: Get the authenticated user. + description: >- + Returns the profile of the user identified by the request's credentials. Useful as a + credential check: a `200` confirms the token is valid and shows whose access it + carries. + tags: + - User + security: + - apiToken: [] + responses: + "200": + description: The authenticated user's profile. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Envelope" + - type: object + required: + - data + properties: + data: + $ref: "#/components/schemas/User" + examples: + default: + summary: An authenticated user's profile. + value: + data: + username: acme-ops + display_name: Acme Operations + company: Acme Corp + email_addresses: + - address: ops@acme.com + is_verified: true + is_primary: true + "401": + $ref: "#/components/responses/Unauthorized" + "405": + $ref: "#/components/responses/MethodNotAllowed" + "500": + $ref: "#/components/responses/InternalServerError" + + /api/v1/deployment-options: + get: + operationId: getDeploymentOptions + summary: List the options a deployment can be created with. + description: >- + Returns the zones, instance types, and storage options available for a cloud, so a + caller can construct a valid `POST /api/v1/deployments` request. + + + The options narrow in steps, because each depends on the one before it. Supply + `cloud` alone for its zones; add `zone` to also get that zone's instance types; add + `instance_type_id` to also get the storage options compatible with that instance. + Fields you haven't narrowed enough to determine are absent rather than empty. + + + Each list is filtered to what you selected: supplying `zone` narrows `zones` to that + zone, and supplying `instance_type_id` narrows `instance_types` to that instance + type. A fully narrowed request therefore describes one combination rather than + repeating the whole catalogue. + + + A `zone` or `instance_type_id` that doesn't exist is a `422` rather than an empty + result, so a typo can't be mistaken for a combination with nothing available. + `instance_type_id` requires `zone`; supplying it alone is a `400`. + + + The `id` of an instance type or storage option is what + `POST /api/v1/deployments` accepts; `name` is for display. + tags: + - Deployment + security: + - apiToken: [] + parameters: + - name: cloud + in: query + required: true + description: The cloud to list options for. + schema: + $ref: "#/components/schemas/CloudProvider" + - name: zone + in: query + required: false + description: >- + A zone from this cloud's `zones`. Supply it to receive `instance_types`. + schema: + type: string + minLength: 1 + example: us-east-1 + - name: instance_type_id + in: query + required: false + description: >- + An instance type `id` from `instance_types`. Supply it, together with `zone`, + to receive `storage_options`. + schema: + type: string + minLength: 1 + example: aws.t2.medium + responses: + "200": + description: The available options, narrowed by the supplied parameters. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Envelope" + - type: object + required: + - data + properties: + data: + $ref: "#/components/schemas/DeploymentOptions" + examples: + default: + summary: Fully narrowed options for one instance type. + value: + data: + cloud: aws + zones: + - us-east-1 + instance_types: + - id: aws.t2.medium + name: t2.medium + cpus: 2 + memory_gb: 4 + description: Trial tier, the lowest spec that runs a Dolt SQL server. + hourly_cost_usd: 0.06849315 + storage_options: + - id: aws.ebs.gp3_50 + name: Trial 50GB EBS + description: Trial tier storage capped at 50GB + min_size_gb: 50 + max_size_gb: 50 + monthly_cost_usd_per_gb: 0 + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "405": + $ref: "#/components/responses/MethodNotAllowed" + "422": + $ref: "#/components/responses/UnprocessableEntity" + "500": + $ref: "#/components/responses/InternalServerError" + "503": + $ref: "#/components/responses/ServiceUnavailable" + + /api/v1/deployments: + post: + operationId: createDeployment + summary: Create a deployment. + description: >- + Provisions a new deployment and returns `202` with the deployment in its `starting` + state. Provisioning continues after the response: poll + `GET /api/v1/deployments/{owner}/{deployment}` until `state` becomes `started`. + + + Deployment names are unique within an owner, so a create is idempotent by name — a + retry after an ambiguous failure returns `409` rather than provisioning a second + deployment. Callers should still treat `409` as "it already exists", not as a + different failure. + + + `instance_type_id` and `volume_type_id` take the **ids** from the deployment options + endpoint, not the display names a deployment reports back on a read. + + + A `5xx` from this endpoint does not guarantee the deployment was *not* created: the + response body is read back after provisioning is accepted, so a failure in that read + surfaces as an error for a create that succeeded. Retrying is the correct response — + it returns `409` if the deployment now exists, and `GET` confirms either way. + + + **Creating a deployment incurs cost.** + tags: + - Deployment + security: + - apiToken: [] + requestBody: + required: true + description: The provisioning parameters for the new deployment. + content: + application/json: + schema: + $ref: "#/components/schemas/CreateDeploymentRequest" + examples: + default: + summary: A single-instance Dolt deployment on AWS. + value: + owner: acme + name: analytics + cloud: aws + zone: us-east-1 + instance_type_id: aws.t2.medium + volume_type_id: aws.ebs.gp3_50 + volume_size_gb: 50 + responses: + "202": + description: >- + The deployment has been accepted and is provisioning. `state` is `starting`. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Envelope" + - type: object + required: + - data + properties: + data: + $ref: "#/components/schemas/Deployment" + examples: + default: + summary: A deployment accepted and provisioning. + value: + data: + owner: acme + name: analytics + state: starting + cloud: aws + zone: us-east-1 + cluster_type: dolt + instance_type_name: t2.medium + volume_type_name: Trial 50GB EBS + volume_size_gb: 50 + replicas: 0 + host: "" + port: 3306 + caller_role: admin + created_by: acme-ops + created_at: "2026-08-11T09:14:00Z" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + "405": + $ref: "#/components/responses/MethodNotAllowed" + "409": + $ref: "#/components/responses/Conflict" + "422": + $ref: "#/components/responses/UnprocessableEntity" + "500": + $ref: "#/components/responses/InternalServerError" + "503": + $ref: "#/components/responses/ServiceUnavailable" + + /api/v1/deployments/{owner}: + get: + operationId: listDeployments + summary: List an owner's deployments. + description: >- + Returns the deployments belonging to `{owner}` that the caller can see, newest + cursor page first. Requires a credential with access to the owner. + + + Items are `DeploymentSummary`, not the full `Deployment` — the backing RPC returns a + narrower shape for lists. Fetch + `GET /api/v1/deployments/{owner}/{deployment}` for the complete resource. + + + Pagination is cursor-based: when `meta.next_page_token` is present, pass it back as + `page_token` to fetch the next page. An absent or empty token means there are no + further results. + tags: + - Deployment + security: + - apiToken: [] + parameters: + - name: owner + in: path + required: true + description: >- + The user or organization whose deployments to list. 3–32 characters of letters, + digits, hyphens, and underscores. + schema: + type: string + pattern: "^[-a-zA-Z0-9_]{3,32}$" + example: acme + - name: page_token + in: query + required: false + description: >- + The `meta.next_page_token` from a previous response. Omit for the first page. + schema: + type: string + example: eyJvZmZzZXQiOjI1fQ + - name: state + in: query + required: false + description: Return only deployments in this state. Omit for all states. + schema: + $ref: "#/components/schemas/DeploymentState" + responses: + "200": + description: The owner's deployments. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Envelope" + - type: object + required: + - data + properties: + data: + type: array + description: The page of deployments. + items: + $ref: "#/components/schemas/DeploymentSummary" + examples: + default: + summary: One page of deployments. + value: + data: + - owner: acme + name: analytics + state: started + cloud: aws + zone: us-west-2 + cluster_type: dolt + instance_type_name: m5.large + volume_type_name: gp3 + volume_size_gb: 100 + replicas: 0 + database_version: 1.58.4 + hourly_cost_usd: 0.192 + webpki_cert: true + meta: + next_page_token: eyJvZmZzZXQiOjI1fQ + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + "404": + $ref: "#/components/responses/NotFound" + "405": + $ref: "#/components/responses/MethodNotAllowed" + "500": + $ref: "#/components/responses/InternalServerError" + + /api/v1/deployments/{owner}/{deployment}: + get: + operationId: getDeployment + summary: Get a deployment. + description: >- + Returns the deployment `{owner}/{deployment}`. Requires a credential with at least + read access; a deployment the caller cannot see returns `404` rather than `403`, so + its existence isn't leaked. + tags: + - Deployment + security: + - apiToken: [] + parameters: + - name: owner + in: path + required: true + description: >- + The user or organization that owns the deployment. 3–32 characters of letters, + digits, hyphens, and underscores. + schema: + type: string + pattern: "^[-a-zA-Z0-9_]{3,32}$" + example: acme + - name: deployment + in: path + required: true + description: >- + The deployment name, unique within the owner. 3–32 characters of letters, + digits, hyphens, and underscores. + schema: + type: string + pattern: "^[-a-zA-Z0-9_]{3,32}$" + example: analytics + responses: + "200": + description: The deployment. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Envelope" + - type: object + required: + - data + properties: + data: + $ref: "#/components/schemas/Deployment" + examples: + default: + summary: A running deployment. + value: + data: + owner: acme + name: analytics + state: started + cloud: aws + zone: us-east-1 + cluster_type: dolt + instance_type_name: t2.medium + volume_type_name: Trial 50GB EBS + volume_size_gb: 50 + replicas: 0 + database_version: 1.58.4 + host: analytics.dbs.hosted.doltdb.com + port: 3306 + hourly_cost_usd: 0.06849315 + webpki_cert: true + expose_remotesapi_endpoint: false + expose_mcp: false + expose_stats: false + disable_automatic_dolt_updates: false + caller_role: admin + created_by: acme-ops + created_at: "2026-07-01T18:22:04Z" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + "405": + $ref: "#/components/responses/MethodNotAllowed" + "500": + $ref: "#/components/responses/InternalServerError" + "503": + $ref: "#/components/responses/ServiceUnavailable" + + /api/v1/deployments/{owner}/{deployment}/instances: + get: + operationId: listDeploymentInstances + summary: List a deployment's instances. + description: >- + Returns the instances backing `{owner}/{deployment}` — one for a single-instance + deployment, or a primary plus its read replicas. + + + Stopped instances are not listed; starting, started, and stopping ones all are. So + an instance appearing here is part of the deployment but not necessarily serving + traffic, and an empty array means it has none outside the stopped state — normal + while a deployment is itself `starting`. + + + The list is not paginated: a deployment has a primary and its replicas, a set small + enough to return whole. + tags: + - Deployment + security: + - apiToken: [] + parameters: + - name: owner + in: path + required: true + description: >- + The user or organization that owns the deployment. 3–32 characters of letters, + digits, hyphens, and underscores. + schema: + type: string + pattern: "^[-a-zA-Z0-9_]{3,32}$" + example: acme + - name: deployment + in: path + required: true + description: >- + The deployment name, unique within the owner. 3–32 characters of letters, + digits, hyphens, and underscores. + schema: + type: string + pattern: "^[-a-zA-Z0-9_]{3,32}$" + example: analytics + responses: + "200": + description: The deployment's non-stopped instances. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Envelope" + - type: object + required: + - data + properties: + data: + type: array + description: The deployment's non-stopped instances. + items: + $ref: "#/components/schemas/DeploymentInstance" + examples: + default: + summary: A primary with one read replica. + value: + data: + - id: 9b1f5c2e-4d3a-4f8b-9c0d-1e2f3a4b5c6d + index: 0 + is_primary: true + host: analytics-0.dbs.hosted.doltdb.com + instance_type_name: t2.medium + volume_type_name: Trial 50GB EBS + volume_size_gb: 50 + hourly_cost_usd: 0.06849315 + - id: 2c4e6a8b-1d3f-4a5c-8e9b-0f1a2b3c4d5e + index: 1 + is_primary: false + host: analytics-1.dbs.hosted.doltdb.com + instance_type_name: t2.medium + volume_type_name: Trial 50GB EBS + volume_size_gb: 50 + hourly_cost_usd: 0.06849315 + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + "405": + $ref: "#/components/responses/MethodNotAllowed" + "500": + $ref: "#/components/responses/InternalServerError" + "503": + $ref: "#/components/responses/ServiceUnavailable" + +components: + headers: + RequestId: + description: >- + The request identifier — echoed on every response (including 2xx). Reuse + the inbound `x-request-id` from the caller / edge proxy when present; + otherwise the server mints a `req_` value. + schema: + type: string + examples: + - req_2f1c9e7a-3b6d-4c5e-8a9f-0d1e2f3a4b5c + + securitySchemes: + apiToken: + type: http + scheme: bearer + description: >- + A Hosted API token, sent as `Authorization: Bearer `. Create one from the + Tokens section of your account settings. Tokens carry the full permissions of the + user who created them and expire on a date chosen at creation. + + schemas: + # --- Error model --------------------------------------------------------------------- + # Every non-2xx response uses Problem (RFC 9457). + ErrorCode: + type: string + title: ErrorCode + description: >- + A stable, machine-readable error code in SCREAMING_SNAKE_CASE. Clients branch on + this value, never on the human-readable `title`/`detail` prose. The baseline codes + below cover the standard HTTP failure categories; endpoint-specific codes (e.g. + `DEPLOYMENT_NOT_FOUND`) are appended to this enum alongside the endpoints that emit + them, which is an additive, non-breaking change under the v1 stability policy. + enum: + - VALIDATION_FAILED # 400 — malformed request or failed input validation + - UNAUTHENTICATED # 401 — missing or invalid credentials + - PERMISSION_DENIED # 403 — authenticated but not permitted + - NOT_FOUND # 404 — resource does not exist + - METHOD_NOT_ALLOWED # 405 — HTTP method not supported for this resource + - CONFLICT # 409 — conflicts with current state (e.g. already exists) + - UNPROCESSABLE # 422 — well-formed but semantically invalid + - RATE_LIMITED # 429 — rate limit exceeded + - INTERNAL # 500 — unexpected server fault + - SERVICE_UNAVAILABLE # 503 — temporarily unavailable + - OPERATION_FAILED # operation reached terminal failure state + examples: + - NOT_FOUND + + Problem: + type: object + title: Problem + description: >- + A structured error body returned for every non-2xx response, following RFC 9457 + (Problem Details for HTTP APIs). This is the single error model for the entire v1 + API — there are no ad-hoc error shapes. + required: + # `type` is required even though RFC 9457 permits omitting it: this API always emits + # it, and leaving it optional made the spec disagree with the generated DTO (which + # marks it non-optional because of the `default`). + - type + - title + - status + - code + - request_id + properties: + type: + type: string + format: uri + default: about:blank + description: >- + A URI identifying the problem type; when dereferenced it points at + human-readable documentation for the error. + examples: + - https://docs.dolthub.com/products/hosted/api/v1/models/#model-errorcode + title: + type: string + description: A short, human-readable summary of the problem type. + examples: + - Not found + status: + type: integer + format: int32 + minimum: 400 + maximum: 599 + description: The HTTP status code, repeated in the body for convenience. + examples: + - 404 + detail: + type: string + description: A human-readable explanation specific to this occurrence of the problem. + examples: + - "Deployment 'analytics' does not exist for owner 'acme'" + instance: + type: string + format: uri-reference + description: A URI reference identifying the specific occurrence (typically the request path). + examples: + - /api/v1/deployments/acme/analytics + code: + $ref: "#/components/schemas/ErrorCode" + request_id: + type: string + description: >- + The request identifier, echoed on every response. Include it when contacting + support so a request can be traced end-to-end. + examples: + - req_01HZX9P7Q5N2M8 + additionalProperties: true + + # --- Success envelope ---------------------------------------------------------------- + Meta: + type: object + title: Meta + description: >- + Response metadata carried alongside the primary `data` payload. All fields are + optional; list endpoints populate `next_page_token` for cursor pagination. + properties: + next_page_token: + type: string + description: >- + Opaque cursor for the next page of a list response. Absent or empty when there + are no further results; otherwise pass it back as the `page_token` query + parameter to fetch the next page. + examples: + - eyJvZmZzZXQiOjI1fQ + additionalProperties: true + examples: + - next_page_token: eyJvZmZzZXQiOjI1fQ + + Envelope: + type: object + title: Envelope + description: >- + The success envelope wrapping every 2xx response body: the resource or list of + resources under `data`, with optional `meta`. This is the single success shape for + the API — there are no unenveloped success bodies. Endpoints narrow `data` to a + concrete resource via `allOf`; the base leaves `data` unconstrained so that + composition works. + required: + - data + properties: + data: + type: [object, array] + # Required for `data` to actually be unconstrained. Without it, openapi-typescript + # emits `Record` — an object permitting no properties — and the + # composed `Envelope & { data: Resource }` becomes unassignable, so nothing can use + # the generated response type. + additionalProperties: true + description: The primary response payload — a resource, or an array of resources for list endpoints. + meta: + $ref: "#/components/schemas/Meta" + examples: + - data: + username: acme-ops + meta: + next_page_token: eyJvZmZzZXQiOjI1fQ + + # --- Resources ---------------------------------------------------------------------- + # Resource schemas are the public DTO surface — what the v1 contract guarantees to + # integrators. Field names are snake_case. Fields are added additively under the v1 + # stability policy; renames or removals require v2. + UserEmailAddress: + type: object + title: UserEmailAddress + description: An email address belonging to a user. + required: + - address + - is_verified + - is_primary + properties: + address: + type: string + description: The email address. + examples: + - ops@acme.com + is_verified: + type: boolean + description: Whether the address has completed email verification. + is_primary: + type: boolean + description: >- + Whether this is the user's primary address. Exactly one address is primary. + User: + type: object + title: User + description: >- + A Hosted user. `GET /api/v1/user` returns the authenticated user's profile. v1 + returns only public-facing profile fields — the identity provider the account signs + in with, session state, and credential metadata are never included. + required: + - username + - email_addresses + properties: + username: + type: string + description: The user's Hosted username (unique handle). + examples: + - acme-ops + display_name: + type: string + description: The user's display name. May be empty. + examples: + - Acme Operations + company: + type: string + description: The user's stated company. May be empty. + examples: + - Acme Corp + email_addresses: + type: array + description: >- + The user's email addresses. Returned only for the authenticated user + themselves; empty for any other caller. + items: + $ref: "#/components/schemas/UserEmailAddress" + + InstanceType: + type: object + title: InstanceType + description: A compute instance type a deployment can run on. + required: + - id + - name + properties: + id: + type: string + description: >- + The identifier `POST /api/v1/deployments` accepts as `instance_type_id`. + examples: + - aws.t2.medium + name: + type: string + description: The display name. A deployment reports this as `instance_type_name`. + examples: + - t2.medium + cpus: + type: integer + format: int32 + description: Virtual CPUs. + examples: + - 2 + memory_gb: + type: integer + format: int32 + description: Memory, in gigabytes. + examples: + - 4 + description: + type: string + description: A human-readable summary of what the instance suits. + examples: + - Trial tier, the lowest spec that runs a Dolt SQL server. + hourly_cost_usd: + type: number + format: float + description: >- + Cost per hour in US dollars. Absent when no hourly price is published for this + instance type. + examples: + - 0.06849315 + + StorageOption: + type: object + title: StorageOption + description: A storage type a deployment's volume can use. + required: + - id + - name + properties: + id: + type: string + description: >- + The identifier `POST /api/v1/deployments` accepts as `volume_type_id`. + examples: + - aws.ebs.gp3_50 + name: + type: string + description: The display name. A deployment reports this as `volume_type_name`. + examples: + - Trial 50GB EBS + description: + type: string + description: A human-readable summary of the storage type. + examples: + - Trial tier storage capped at 50GB + min_size_gb: + type: integer + format: int32 + description: >- + Smallest volume size this type supports, in gigabytes. `volume_size_gb` on a + create request must be at least this. + examples: + - 50 + max_size_gb: + type: integer + format: int32 + description: >- + Largest volume size this type supports, in gigabytes. + examples: + - 50 + monthly_cost_usd_per_gb: + type: number + format: float + description: Cost per gigabyte per month, in US dollars. + examples: + - 0 + + DeploymentInstance: + type: object + title: DeploymentInstance + description: >- + One instance backing a deployment. A deployment has a primary and, when it has read + replicas, one instance per replica. + required: + - id + - index + - is_primary + properties: + id: + type: string + format: uuid + description: The instance's identifier, unique within the deployment. + examples: + - 9b1f5c2e-4d3a-4f8b-9c0d-1e2f3a4b5c6d + index: + type: integer + format: int32 + description: >- + The instance's position in the deployment. `0` is the first instance; replicas + take the following indices. + examples: + - 0 + is_primary: + type: boolean + description: >- + Whether this instance is currently the primary. Exactly one instance of a + started deployment is primary, and which one can change over the deployment's + life. + examples: + - true + host: + type: string + description: >- + The hostname for this specific instance. Connect to the deployment's own `host` + unless you mean to address one instance directly. + examples: + - analytics-0.dbs.hosted.doltdb.com + instance_type_name: + type: string + description: The display name of this instance's type. + examples: + - t2.medium + volume_type_name: + type: string + description: The display name of this instance's storage type. + examples: + - Trial 50GB EBS + volume_size_gb: + type: integer + format: int32 + description: The size of this instance's storage volume, in gigabytes. + examples: + - 50 + hourly_cost_usd: + type: number + format: float + description: >- + This instance's cost per hour, in US dollars. A deployment's total is the sum + across its instances. + examples: + - 0.06849315 + + DeploymentOptions: + type: object + title: DeploymentOptions + description: >- + The options available for creating a deployment, narrowed by the query parameters + supplied. `instance_types` and `storage_options` are absent until enough of the + chain has been supplied to determine them. + required: + - cloud + - zones + properties: + cloud: + $ref: "#/components/schemas/CloudProvider" + zones: + type: array + description: The zones this cloud supports. + items: + type: string + examples: + - ["us-east-1", "us-west-2", "eu-west-2"] + instance_types: + type: array + description: >- + Instance types available in the requested `zone`. Absent when `zone` wasn't + supplied. + items: + $ref: "#/components/schemas/InstanceType" + storage_options: + type: array + description: >- + Storage types compatible with the requested `instance_type_id`. Absent when + `zone` and `instance_type_id` weren't both supplied. + items: + $ref: "#/components/schemas/StorageOption" + + CreateDeploymentRequest: + type: object + title: CreateDeploymentRequest + description: >- + The provisioning parameters for a new deployment. + + + v1.0 exposes the core parameters only. Restoring from a backup, cloning an existing + deployment, workbench user settings, and private networking are all settable on the + internal API but are not part of this request; each is its own feature with its own + contract, and adding them is additive. The internal deployment test flag is + deliberately not exposed at all. + required: + - owner + - name + - cloud + - zone + - instance_type_id + - volume_type_id + - volume_size_gb + properties: + owner: + type: string + description: >- + The user or organization that will own the deployment. The caller must have + permission to create deployments for it. 3–32 characters of letters, digits, + hyphens, and underscores. + pattern: "^[-a-zA-Z0-9_]{3,32}$" + examples: + - acme + name: + type: string + description: >- + The deployment name, unique within the owner. 3–32 characters of letters, + digits, hyphens, and underscores. + pattern: "^[-a-zA-Z0-9_]{3,32}$" + examples: + - analytics + cloud: + $ref: "#/components/schemas/CloudProvider" + zone: + type: string + description: The cloud region to provision in, as listed by the deployment options. + minLength: 1 + examples: + - us-east-1 + cluster_type: + $ref: "#/components/schemas/ClusterType" + instance_type_id: + type: string + description: >- + The **id** of the instance type, from the deployment options endpoint. Note a + deployment reports `instance_type_name` on a read — the id and the display name + are different values. + minLength: 1 + examples: + - aws.t2.medium + volume_type_id: + type: string + description: >- + The **id** of the storage type, from the deployment options endpoint. As with + `instance_type_id`, this is the id rather than the display name. + minLength: 1 + examples: + - aws.ebs.gp3_50 + volume_size_gb: + type: integer + format: int32 + description: >- + The size of the storage volume, in gigabytes. Must fall within the selected + storage type's supported range. + minimum: 1 + examples: + - 50 + replicas: + type: integer + format: int32 + description: The number of read replicas. Defaults to `0` when omitted. + minimum: 0 + examples: + - 2 + webpki_cert: + type: boolean + description: >- + Serve a publicly-trusted (WebPKI) TLS certificate rather than a Hosted-issued + one. Defaults to `false` when omitted. + examples: + - true + expose_remotesapi_endpoint: + type: boolean + description: Expose a Dolt remotes API endpoint. Defaults to `false` when omitted. + examples: + - false + expose_mcp: + type: boolean + description: Expose an MCP endpoint. Defaults to `false` when omitted. + examples: + - false + expose_stats: + type: boolean + description: Expose a statistics endpoint. Defaults to `false` when omitted. + examples: + - false + + DeploymentState: + type: string + title: DeploymentState + description: >- + The deployment's lifecycle state. `starting` covers both initial provisioning and + a restart; poll this field to observe a create or a resize reaching `started`. + enum: + - starting + - started + - stopping + - stopped + examples: + - started + + CloudProvider: + type: string + title: CloudProvider + description: The cloud the deployment runs in. + enum: + - aws + - gcp + - azure + examples: + - aws + + ClusterType: + type: string + title: ClusterType + description: >- + The database engine the deployment runs. `mysql_with_dolt_replicas` is a MySQL + primary with Dolt read replicas. + enum: + - dolt + - doltgres + - mysql_with_dolt_replicas + examples: + - dolt + + DeploymentRole: + type: string + title: DeploymentRole + description: >- + The authenticated caller's role on this deployment. Always present on a read, since + a caller without at least read access cannot retrieve the deployment at all. + enum: + - admin + - writer + - reader + - reader_and_pulls + examples: + - admin + + DeploymentSummary: + type: object + title: DeploymentSummary + description: >- + A deployment as it appears in a list. + + + This is deliberately not the same shape as `Deployment`. The list RPC returns a + narrower record — it omits connection details, the caller's role, and the creation + audit fields, and it adds the last-backup figures shown in fleet views. Read the + deployment itself for the full resource. As with `Deployment`, database credentials + are never included. + required: + - owner + - name + - state + - cloud + - zone + - cluster_type + properties: + owner: + type: string + description: The user or organization that owns the deployment. + examples: + - acme + name: + type: string + description: The deployment name, unique within the owner. + examples: + - analytics + state: + $ref: "#/components/schemas/DeploymentState" + cloud: + $ref: "#/components/schemas/CloudProvider" + zone: + type: string + description: The cloud region the deployment runs in. + examples: + - us-west-2 + cluster_type: + $ref: "#/components/schemas/ClusterType" + instance_type_name: + type: string + description: The display name of the deployment's instance type. + examples: + - m5.large + volume_type_name: + type: string + description: The display name of the deployment's storage type. + examples: + - gp3 + volume_size_gb: + type: integer + format: int32 + description: The size of the deployment's storage volume, in gigabytes. + examples: + - 100 + replicas: + type: integer + format: int32 + description: The number of read replicas. `0` for a single-instance deployment. + examples: + - 0 + database_version: + type: string + description: >- + The version of the database engine the deployment is running — a Dolt version + for a `dolt` cluster, a Doltgres version for a `doltgres` one. + examples: + - 1.58.4 + hourly_cost_usd: + type: number + format: float + description: The deployment's current cost per hour, in US dollars. + examples: + - 0.192 + webpki_cert: + type: boolean + description: >- + Whether the deployment serves a publicly-trusted (WebPKI) TLS certificate. + examples: + - true + last_backup_size_bytes: + type: integer + format: int64 + description: >- + Size of the most recent backup, in bytes. Absent when no backup has been taken + or its size has not been computed yet. + examples: + - 1048576 + last_backup_time: + type: string + format: date-time + description: >- + When the most recent backup was taken. Absent when no backup has been taken. + examples: + - "2026-08-10T02:00:00Z" + + Deployment: + type: object + title: Deployment + description: >- + A Hosted Dolt deployment. + + + v1 returns configuration and lifecycle state only. The deployment's database + credentials are deliberately **not** part of this resource — they are issued and + rotated through the deployment credentials endpoints, so that reading a deployment + is never a credential-disclosing operation. + + + Provider-specific private-networking configuration (AWS PrivateLink, GCP Private + Service Connect, Azure Private Link) is not included in v1.0. Each carries its own + endpoint collection and provisioning state, and will land as its own sub-resource; + adding it is additive under the stability policy. + required: + - owner + - name + - state + - cloud + - zone + - cluster_type + - caller_role + - created_at + properties: + owner: + type: string + description: The user or organization that owns the deployment. + examples: + - acme + name: + type: string + description: The deployment name, unique within the owner. + examples: + - analytics + state: + $ref: "#/components/schemas/DeploymentState" + cloud: + $ref: "#/components/schemas/CloudProvider" + zone: + type: string + description: The cloud region the deployment runs in. + examples: + - us-east-1 + cluster_type: + $ref: "#/components/schemas/ClusterType" + instance_type_name: + type: string + description: >- + The display name of the deployment's instance type. Note this is the *name*, not + the id that `POST /api/v1/deployments` accepts; both are listed by the deployment + options endpoint. + examples: + - t2.medium + volume_type_name: + type: string + description: >- + The display name of the deployment's storage type. As with `instance_type_name`, + this is the name rather than the id used to create a deployment. + examples: + - Trial 50GB EBS + volume_size_gb: + type: integer + format: int32 + description: The size of the deployment's storage volume, in gigabytes. + examples: + - 50 + replicas: + type: integer + format: int32 + description: The number of read replicas. `0` for a single-instance deployment. + examples: + - 0 + database_version: + type: string + description: >- + The version of the database engine the deployment is running — a Dolt version + for a `dolt` cluster, a Doltgres version for a `doltgres` one. + examples: + - 1.58.4 + host: + type: string + description: >- + The hostname clients connect to. Empty until the deployment reaches `started`. + examples: + - analytics.dbs.hosted.doltdb.com + port: + type: integer + format: int32 + description: The port clients connect to. + examples: + - 3306 + hourly_cost_usd: + type: number + format: float + description: The deployment's current cost per hour, in US dollars. + examples: + - 0.06849315 + webpki_cert: + type: boolean + description: >- + Whether the deployment serves a publicly-trusted (WebPKI) TLS certificate rather + than a Hosted-issued one. + expose_remotesapi_endpoint: + type: boolean + description: Whether the deployment exposes a Dolt remotes API endpoint. + expose_mcp: + type: boolean + description: Whether the deployment exposes an MCP endpoint. + expose_stats: + type: boolean + description: Whether the deployment exposes a statistics endpoint. + disable_automatic_dolt_updates: + type: boolean + description: >- + Whether automatic Dolt version updates are disabled for this deployment. + caller_role: + $ref: "#/components/schemas/DeploymentRole" + created_by: + type: string + description: The username of the user who created the deployment. + examples: + - acme-ops + created_at: + type: string + format: date-time + description: When the deployment was created. + examples: + - "2026-07-01T18:22:04Z" + destroy_at: + type: string + format: date-time + description: >- + When the deployment is scheduled to be destroyed. Absent unless a destroy has + been scheduled. + examples: + - "2026-09-01T00:00:00Z" + destroyed_by: + type: string + description: >- + The username of the user who destroyed the deployment. Absent unless it has been + destroyed. + examples: + - acme-ops + + responses: + BadRequest: + description: The request was malformed or failed input validation. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + Unauthorized: + description: Authentication credentials were missing or invalid. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + Forbidden: + description: Authenticated, but not permitted to perform this action. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + NotFound: + description: The requested resource does not exist. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + MethodNotAllowed: + description: The HTTP method is not supported for this resource. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + Allow: + description: The HTTP methods supported by this resource. + schema: + type: string + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + Conflict: + description: The request conflicts with the current state of the resource (e.g. it already exists). + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + UnprocessableEntity: + description: The request was well-formed but semantically invalid. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + TooManyRequests: + description: The client has exceeded its rate limit. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + Retry-After: + description: The number of seconds to wait before retrying the request. + schema: + type: integer + format: int32 + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + InternalServerError: + description: An unexpected server error occurred. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + ServiceUnavailable: + description: The service is temporarily unavailable. + headers: + x-request-id: + $ref: "#/components/headers/RequestId" + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" From d15a9d011f9ef9d4631e42fa22c511aa7237617e Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Fri, 14 Aug 2026 12:07:28 -0700 Subject: [PATCH 03/12] Update the hosted v1 spec from ld main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-vendors specs/hosted-v1.yaml from ld main (1c62599829e), replacing the copy taken from the taylor/hosted-api-v1-list-instances branch, and regenerates deployment.md and models.md from it. The changes are all prose — no endpoints, schemas, or fields moved (still 6 operations and 17 schemas). Two are rewordings of the create-deployment 5xx guidance and the pagination description. The third is a real contract detail: next_page_token is now documented as absent on the last page rather than "absent or empty", and `meta` is omitted entirely there. That last one also invalidated a hand-written sentence in v1/README.md, which told clients to treat an empty token as the end of results. Updated to match: presence of the token is the only check needed. Co-Authored-By: Claude Opus 5 (1M context) --- .../content/products/hosted/api/v1/README.md | 2 +- .../products/hosted/api/v1/deployment.md | 4 ++-- .../content/products/hosted/api/v1/models.md | 2 +- specs/hosted-v1.yaml | 17 ++++++++--------- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/site/dolt/src/content/products/hosted/api/v1/README.md b/site/dolt/src/content/products/hosted/api/v1/README.md index 69e1987..6102f9f 100644 --- a/site/dolt/src/content/products/hosted/api/v1/README.md +++ b/site/dolt/src/content/products/hosted/api/v1/README.md @@ -66,7 +66,7 @@ List endpoints put the pagination cursor in `meta`: } ``` -When `meta.next_page_token` is present, pass it back as the `page_token` query parameter to fetch the next page. An absent or empty token means there are no further results. +When `meta.next_page_token` is present, pass it back as the `page_token` query parameter to fetch the next page. On the last page `meta` is omitted entirely, so checking whether the token is present is all a client needs — it is never returned present but empty. ## Errors diff --git a/site/dolt/src/content/products/hosted/api/v1/deployment.md b/site/dolt/src/content/products/hosted/api/v1/deployment.md index 21bfa61..b6b5a32 100644 --- a/site/dolt/src/content/products/hosted/api/v1/deployment.md +++ b/site/dolt/src/content/products/hosted/api/v1/deployment.md @@ -92,7 +92,7 @@ Deployment names are unique within an owner, so a create is idempotent by name `instance_type_id` and `volume_type_id` take the **ids** from the deployment options endpoint, not the display names a deployment reports back on a read. -A `5xx` from this endpoint does not guarantee the deployment was *not* created: the response body is read back after provisioning is accepted, so a failure in that read surfaces as an error for a create that succeeded. Retrying is the correct response — it returns `409` if the deployment now exists, and `GET` confirms either way. +As with any create, a `5xx` or a dropped connection does not tell you whether the deployment was created — the call can succeed remotely and fail on the way back. Retry: it returns `409` if the deployment now exists, and `GET` confirms either way. **Creating a deployment incurs cost.** @@ -171,7 +171,7 @@ Returns the deployments belonging to `{owner}` that the caller can see, newest c Items are `DeploymentSummary`, not the full `Deployment` — the backing RPC returns a narrower shape for lists. Fetch `GET /api/v1/deployments/{owner}/{deployment}` for the complete resource. -Pagination is cursor-based: when `meta.next_page_token` is present, pass it back as `page_token` to fetch the next page. An absent or empty token means there are no further results. +Pagination is cursor-based: when `meta.next_page_token` is present, pass it back as `page_token` to fetch the next page. On the last page `meta` is omitted entirely, so presence of the token is the only check a client needs. **Parameters** diff --git a/site/dolt/src/content/products/hosted/api/v1/models.md b/site/dolt/src/content/products/hosted/api/v1/models.md index fc32931..8a498e2 100644 --- a/site/dolt/src/content/products/hosted/api/v1/models.md +++ b/site/dolt/src/content/products/hosted/api/v1/models.md @@ -48,7 +48,7 @@ Response metadata carried alongside the primary `data` payload. All fields are o | Field | Type | Required | Description | |-------|------|----------|-------------| -| `next_page_token` | `string` | no | Opaque cursor for the next page of a list response. Absent or empty when there are no further results; otherwise pass it back as the `page_token` query parameter to fetch the next page. | +| `next_page_token` | `string` | no | Opaque cursor for the next page of a list response. Absent when there are no further results — never present and empty — otherwise pass it back as the `page_token` query parameter to fetch the next page. | --- diff --git a/specs/hosted-v1.yaml b/specs/hosted-v1.yaml index 6f05f9e..56bb91c 100644 --- a/specs/hosted-v1.yaml +++ b/specs/hosted-v1.yaml @@ -230,10 +230,9 @@ paths: endpoint, not the display names a deployment reports back on a read. - A `5xx` from this endpoint does not guarantee the deployment was *not* created: the - response body is read back after provisioning is accepted, so a failure in that read - surfaces as an error for a create that succeeded. Retrying is the correct response — - it returns `409` if the deployment now exists, and `GET` confirms either way. + As with any create, a `5xx` or a dropped connection does not tell you whether the + deployment was created — the call can succeed remotely and fail on the way back. + Retry: it returns `409` if the deployment now exists, and `GET` confirms either way. **Creating a deployment incurs cost.** @@ -329,8 +328,8 @@ paths: Pagination is cursor-based: when `meta.next_page_token` is present, pass it back as - `page_token` to fetch the next page. An absent or empty token means there are no - further results. + `page_token` to fetch the next page. On the last page `meta` is omitted entirely, so + presence of the token is the only check a client needs. tags: - Deployment security: @@ -719,9 +718,9 @@ components: next_page_token: type: string description: >- - Opaque cursor for the next page of a list response. Absent or empty when there - are no further results; otherwise pass it back as the `page_token` query - parameter to fetch the next page. + Opaque cursor for the next page of a list response. Absent when there are no + further results — never present and empty — otherwise pass it back as the + `page_token` query parameter to fetch the next page. examples: - eyJvZmZzZXQiOjI1fQ additionalProperties: true From 417148bf5d9f2139df2cd04ab9cd9a8e65e44ff5 Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Mon, 17 Aug 2026 10:51:46 -0700 Subject: [PATCH 04/12] Flatten the hosted API nav, keeping /v1/ in the URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sections sit directly under API rather than behind a "v1" node — with one version shipped, that layer was a click with nothing to choose. Every path still carries /v1/, so adding a v2 later reintroduces the version layer without moving any of these URLs. The v1 overview page is kept in the tree as "Overview". It holds the endpoint table, response envelope, error model, and stability policy, so dropping the v1 node without it would have left that page reachable only by inbound link. Co-Authored-By: Claude Opus 5 (1M context) --- site/dolt/src/nav.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/site/dolt/src/nav.ts b/site/dolt/src/nav.ts index cd3d1af..1b82e75 100644 --- a/site/dolt/src/nav.ts +++ b/site/dolt/src/nav.ts @@ -245,19 +245,17 @@ const nav: NavSection[] = [ { title: "Private Networking", href: "/products/hosted/private-networking" }, { title: "SSO", href: "/products/hosted/sso" }, { + // Only one API version exists, so the sections sit directly under + // API rather than behind a "v1" node. The paths keep /v1/ so a + // future v2 can be added without moving any of these URLs. title: "API", href: "/products/hosted/api", children: [ - { - title: "v1", - href: "/products/hosted/api/v1", - children: [ - { title: "Authentication", href: "/products/hosted/api/v1/authentication" }, - { title: "User", href: "/products/hosted/api/v1/user" }, - { title: "Deployment", href: "/products/hosted/api/v1/deployment" }, - { title: "Models", href: "/products/hosted/api/v1/models" }, - ], - }, + { title: "Overview", href: "/products/hosted/api/v1" }, + { title: "Authentication", href: "/products/hosted/api/v1/authentication" }, + { title: "User", href: "/products/hosted/api/v1/user" }, + { title: "Deployment", href: "/products/hosted/api/v1/deployment" }, + { title: "Models", href: "/products/hosted/api/v1/models" }, ], }, ], From 5f4b72ec1c7ed0690bb168f6f68364a1339c1437 Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Mon, 17 Aug 2026 11:23:36 -0700 Subject: [PATCH 05/12] Restore the heading hierarchy on hosted notable-features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Operations, Quick Start, SQL Workbench, and Dolt Ecosystem were rendering at the same level as their own subsections, so Logs and Monitoring looked like peers of Operations rather than parts of it, and the right-side table of contents was 24 flat entries. The levels come from 4276c04, which demoted each page's section headings from h1 to h2 but left their existing h2 subsections alone, collapsing the two onto one level. Restored by mapping every heading back to its level at the original import (90815da) plus one, since ccc4760 moved the page h1 into frontmatter — so the five sections stay h2 and their 19 subsections become h3. Heading lines only; no prose changed. Anchor ids are unaffected: rehype-slug derives them from heading text, not level, so existing deep links still resolve. Co-Authored-By: Claude Opus 5 (1M context) --- .../products/hosted/notable-features.md | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/site/dolt/src/content/products/hosted/notable-features.md b/site/dolt/src/content/products/hosted/notable-features.md index 48a38c4..93b0364 100644 --- a/site/dolt/src/content/products/hosted/notable-features.md +++ b/site/dolt/src/content/products/hosted/notable-features.md @@ -18,21 +18,21 @@ example: ## Operations -## Logs +### Logs While your database is running we collect logging so you can monitor your database. View and download logs from any time period from the deployment console. ![](../../.gitbook/assets/hosted-logs.png) -## Monitoring +### Monitoring We also collect metrics on your running database and display graphs so you can see performance and usage in real time. ![](../../.gitbook/assets/hosted-monitoring.png) -## Custom Configuration +### Custom Configuration Dolt has a myriad of [configuration options](/sql-reference/server/configuration). Most of these can be changed on the running server by setting the appropriate [system @@ -41,14 +41,14 @@ provides a simple UI for viewing and changing your deployment's custom configura ![](../../.gitbook/assets/hosted-custom-config.png) -## Replication +### Replication Scale your deployment read capacity or create hot failover instances by enabling read replication for your deployment. Configure how many replicas you want. You'll get logs and metrics for each server. ![](../../.gitbook/assets/hosted-replica-graphs.png) -## Backups +### Backups Full copies of all your databases are backed up nightly. These backups are kept for 14 days. @@ -66,14 +66,14 @@ requires you to create a support ticket. Learn more about backups [here](https://www.dolthub.com/blog/2022-08-31-hosted-backups/). -## Private Networking +### Private Networking Want to use Hosted Dolt, but don't want your database to be reachable through the public internet. We support both AWS and GCP private networking. [Private networking setup instructions](/products/hosted/private-networking) -## Dolt Upgrades +### Dolt Upgrades We're releasing [new versions of Dolt](https://github.com/dolthub/dolt/releases) at least once per week. Set your service window and your deployed Dolt version will automatically @@ -84,7 +84,7 @@ get upgraded weekly to the latest available version with minimal downtime. Or if you want to upgrade Dolt immediately, you can use the "Update Dolt version now" button in the Actions dropdown. -## Enterprise Support +### Enterprise Support Hosted Dolt comes with enterprise support. [Our team](https://www.dolthub.com/team) of veteran cloud service engineers will keep your database operating smoothly. @@ -97,7 +97,7 @@ will get back to you within the hour. Learn more about our support ticket system [here](https://www.dolthub.com/blog/2022-07-13-hosted-dolt-incident-manager/). -## Access Management +### Access Management Easily manage access to your deployment using the same permissions model as [DoltHub](/concepts/dolthub/permissions) and @@ -121,7 +121,7 @@ a Branch Permissions UI. See ## Quick Start -## Trial Instance +### Trial Instance To lower the cost barrier, Hosted Dolt offers [trial instance](https://www.dolthub.com/blog/2022-10-24-hosted-trial-instances/) for $50 a @@ -129,7 +129,7 @@ month. For $50 you get a t2.medium running in EC2 with 50GB of storage. There is limit for these instances. This isn't a 30-day trial. It's an instance perfect for trialing Dolt. -## Connect Using Any MySQL Client +### Connect Using Any MySQL Client Dolt is a drop in replacement for MySQL. You can connect to your instance from anywhere using your favorite MySQL client using the connectivity information in the Connectivity @@ -139,7 +139,7 @@ tab of your deployment console. ## SQL Workbench -## User Friendly Built-In Web GUI +### User Friendly Built-In Web GUI Each deployment comes with a built-in SQL workbench, inspired by [DoltHub's](https://www.dolthub.com) database page. Browse your data in read-only mode or @@ -151,31 +151,31 @@ favorite DoltHub features, including [pull requests](#pull-requests), [diffs](#d Learn more about the SQL workbench [here](/products/hosted/sql-workbench). -## Pull Requests +### Pull Requests Create pull requests for human review of changes to your database. ![](../../.gitbook/assets/hosted-workbench-pr.png) -## Diffs +### Diffs Analyze your data and schema changes in a diff and debug issues in specific commits. ![](../../.gitbook/assets/hosted-workbench-pr-diff.png) -## Commit Log +### Commit Log View an audit log of all changes made to your data. ![](../../.gitbook/assets/hosted-workbench-commit-log.png) -## ER Diagrams +### ER Diagrams Visualize the entities in your database as well as the relationship between tables. ![](../../.gitbook/assets/hosted-workbench-er-diagram.png) -## CSV Upload and Download +### CSV Upload and Download Update database tables using an uploaded CSV or the built-in spreadsheet editor. @@ -189,7 +189,7 @@ Learn more in [our blog](https://www.dolthub.com/blog/2023-06-30-hosted-workbenc ## Dolt Ecosystem -## Clone a Hosted Instance +### Clone a Hosted Instance In some cases you might want to clone your database from Hosted Dolt so that you can access Dolt's [command line interface](/cli-reference/cli). Hosted provides an option @@ -200,7 +200,7 @@ Hosted. Learn more in our [cloning guide](/products/hosted/cloning). -## Use DoltHub as a Remote +### Use DoltHub as a Remote To interact with DoltHub from your Hosted instance, you can use DoltHub as a [remote](/concepts/dolt/git/remotes). We have [SQL From 9d0381c6bc77a36be08396c95f9d845312afe67a Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Mon, 17 Aug 2026 11:28:36 -0700 Subject: [PATCH 06/12] Restore the heading hierarchy on the use-case pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same collapse as hosted notable-features: 4276c04 demoted each page's section headings from h1 to h2 without demoting their existing h2 subsections, so the alternatives under "Dolt replaces…" — Soft Deletes, Change Data Capture, Files in Git, Spreadsheets, and the rest — rendered as peers of the section that introduces them instead of items within it. Recovered the same way: each heading mapped back to its level immediately before 4276c04, plus one for the page h1 that ccc4760 moved into frontmatter. Every heading matched that baseline, so the mapping was mechanical rather than a judgement call. 13 headings across 8 files; heading lines only, no prose touched, and no file ends up skipping a level. Anchor ids are unchanged — rehype-slug derives them from heading text. Co-Authored-By: Claude Opus 5 (1M context) --- site/dolt/src/content/introduction/use-cases/audit.md | 4 ++-- .../introduction/use-cases/configuration-management.md | 2 +- .../content/introduction/use-cases/data-and-model-quality.md | 4 ++-- site/dolt/src/content/introduction/use-cases/data-sharing.md | 4 ++-- .../content/introduction/use-cases/manual-data-curation.md | 2 +- site/dolt/src/content/introduction/use-cases/offline-first.md | 2 +- site/dolt/src/content/introduction/use-cases/vc-your-app.md | 4 ++-- .../src/content/introduction/use-cases/versioned-replica.md | 4 ++-- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/site/dolt/src/content/introduction/use-cases/audit.md b/site/dolt/src/content/introduction/use-cases/audit.md index 3fdd041..de32013 100644 --- a/site/dolt/src/content/introduction/use-cases/audit.md +++ b/site/dolt/src/content/introduction/use-cases/audit.md @@ -23,11 +23,11 @@ If you're not ready to switch your primary database to Dolt to get its audit cap ## Dolt replaces... -## Soft Deletes +### Soft Deletes A technique to add audit capability to an existing database is to add [soft deletes](https://www.dolthub.com/blog/2022-11-03-soft-deletes/). Soft delete is the use various techniques to mark data as inactive instead of deleting it. This is strictly worse than a version controlled database for audit purposes. With soft deletes, an operator can still modify data or the application can make mistakes. In Dolt, every write is part of the audit log. It is far more difficult for an operator to change Dolt history. -## Change Data Capture +### Change Data Capture [Change Data Capture](https://www.dolthub.com/blog/2023-03-01-change-data-capture/) is another way to add audit capability to an existing database. Some change data capture techniques are similar to [soft delete](https://www.dolthub.com/blog/2022-11-03-soft-deletes/) strategies. Modern change data capture tools consume replication logs to audit database changes. Dolt can consume the same logs in the [versioned MySQL replica use case](/introduction/use-cases/versioned-replica) producing a simpler and thus, more audit-friendly, change data capture solution. diff --git a/site/dolt/src/content/introduction/use-cases/configuration-management.md b/site/dolt/src/content/introduction/use-cases/configuration-management.md index 51a5dbe..94f00cb 100644 --- a/site/dolt/src/content/introduction/use-cases/configuration-management.md +++ b/site/dolt/src/content/introduction/use-cases/configuration-management.md @@ -24,7 +24,7 @@ This use case is particularly popular in video games where much of the game func ## Dolt replaces... -## Files in Git +### Files in Git Most large configuration files are stored and versioned in Git. If the files get too large they are store in cloud storage and linked to Git using [git-lfs](https://git-lfs.com/). If the files are stored in git-lfs, you lose the ability to diff the contents of the files. Dolt improves the experience by adding query capabilities and large fine-grained diffs to the data stored in configuration files. The diff and merge experience will be greatly improved in Dolt for this type of data. diff --git a/site/dolt/src/content/introduction/use-cases/data-and-model-quality.md b/site/dolt/src/content/introduction/use-cases/data-and-model-quality.md index 5d66065..de86369 100644 --- a/site/dolt/src/content/introduction/use-cases/data-and-model-quality.md +++ b/site/dolt/src/content/introduction/use-cases/data-and-model-quality.md @@ -31,11 +31,11 @@ Lastly, [commits](/concepts/dolt/git/commits), [logs](/concepts/dolt/git/log), a ## Dolt replaces... -## Unstructured files in cloud storage +### Unstructured files in cloud storage It is common practice to store copies of training data or database backups in cloud storage for model reproducibility. A full copy of the data is stored for every training run. This can become quite expensive and limit the amount of models you can reproduce. Dolt stores only the differences between stored versions decreasing the cost of data storage. Additionally, Dolt can produce diffs between versions of training data producing novel model insights. -## MySQL, Postgres, or other databases +### MySQL, Postgres, or other databases Dolt can replace any database used to store and query data. Many of our customers switch from other OLTP databases like MySQL or Postgres to improve data and model quality through versioning. Customers have also switched to Dolt from document databases like MongoDB. Dolt's additional unique features like branches, diffs, and merges allow for human review of data changes and multiple parallel data projects. diff --git a/site/dolt/src/content/introduction/use-cases/data-sharing.md b/site/dolt/src/content/introduction/use-cases/data-sharing.md index 892cea5..bf5352d 100644 --- a/site/dolt/src/content/introduction/use-cases/data-sharing.md +++ b/site/dolt/src/content/introduction/use-cases/data-sharing.md @@ -27,11 +27,11 @@ Dolt and DoltHub are also great if vendors share data with you. When you receive ## Dolt replaces... -## Exchanging Files +### Exchanging Files Dolt replaces exchanging flat data files like CSVs via email, FTP servers, or other file transfer techniques. Dolt allows data to maintain schema on exchange including constraints, triggers, and views. This more rich format of exchange reduces transfer errors. Dolt also allows you to change the data to fit your needs and still get updates from your source. Dolt will notify you if your changes [conflict](/concepts/dolt/git/conflicts) with the source. -## External APIs +### External APIs Dolt is ideal for sharing data that does not have an API. But even for data with an API, Dolt is often more convenient. With Dolt, you get all the data and its history. With APIs you often have to assemble the data with multiple API calls. With APIs, the data can change out from under you, whereas with Dolt you can read a version of the data until you are ready to upgrade. DoltHub ships with a [SQL API](/products/dolthub/api/v1alpha1/sql) so you can choose the data sharing solution that is right for your use case. diff --git a/site/dolt/src/content/introduction/use-cases/manual-data-curation.md b/site/dolt/src/content/introduction/use-cases/manual-data-curation.md index f580c2e..05ffffa 100644 --- a/site/dolt/src/content/introduction/use-cases/manual-data-curation.md +++ b/site/dolt/src/content/introduction/use-cases/manual-data-curation.md @@ -20,7 +20,7 @@ Dolt is a MySQL compatible database so exporting the manually created data to pr ## Dolt replaces... -## Spreadsheets +### Spreadsheets Dolt replaces Excel or Google Sheets for manual data curation. Versioning features allow for more efficient asynchronous collaboration and human review of data changes. The DoltHub interface is still easy enough for non-technical users to contribute and review data changes. diff --git a/site/dolt/src/content/introduction/use-cases/offline-first.md b/site/dolt/src/content/introduction/use-cases/offline-first.md index 5f6980c..fadc87d 100644 --- a/site/dolt/src/content/introduction/use-cases/offline-first.md +++ b/site/dolt/src/content/introduction/use-cases/offline-first.md @@ -23,7 +23,7 @@ Conflicting writes are surfaced quickly and an operator or software can take add ## Dolt replaces -## Custom syncing processes +### Custom syncing processes Dolt replaces custom code to synchronize your client and server. This code is complicated and hard to get right. The Git [remote](/concepts/dolt/git/remotes) model of clone, fetch, push, and pull is a proven synchronization model. Dolt brings this model to the database allowing you to remove most of your synchronization code. diff --git a/site/dolt/src/content/introduction/use-cases/vc-your-app.md b/site/dolt/src/content/introduction/use-cases/vc-your-app.md index 53c45ea..1e7d0d1 100644 --- a/site/dolt/src/content/introduction/use-cases/vc-your-app.md +++ b/site/dolt/src/content/introduction/use-cases/vc-your-app.md @@ -25,11 +25,11 @@ In the past applications that needed these features required [slowly changing di ## Dolt replaces -## Soft Deletes +### Soft Deletes A common technique to version your database is to use [soft deletes](https://www.dolthub.com/blog/2022-11-03-soft-deletes/). When your application would make an update or a delete, you application instead makes an insert and marks the old row invalid. Dolt obviates the need for this technique. You can keep your existing database schema and Dolt ensures every write is non-destructive. Queries against soft deleted rows become Dolt history queries against [system tables](/sql-reference/version-control/dolt-system-tables). -## Slowly Changing Dimension +### Slowly Changing Dimension A more advanced technique for versioning databases is [slowly changing dimension](https://www.dolthub.com/blog/2023-06-22-slowly-changing-dimension/). Slowly Changing Dimension is similar to soft deletes. Additional database columns are added to tables to manage versioning. Dolt is slowly changing dimension on every table by default. Queries involving the slowly changing dimension become Dolt history queries against [system tables](/sql-reference/version-control/dolt-system-tables). Moreover, complicated [merge](/concepts/dolt/git/merge) processes can happen at the database layer. Merges must handled by custom code at the application layer with slowly changing dimension. diff --git a/site/dolt/src/content/introduction/use-cases/versioned-replica.md b/site/dolt/src/content/introduction/use-cases/versioned-replica.md index 86d6c94..ead0cec 100644 --- a/site/dolt/src/content/introduction/use-cases/versioned-replica.md +++ b/site/dolt/src/content/introduction/use-cases/versioned-replica.md @@ -25,11 +25,11 @@ Additionally, a Dolt replica can be easily cloned (ie. copied) to a developer's ## Dolt replaces... -## Backups and Transaction Logs +### Backups and Transaction Logs Dolt as a versioned replica becomes your first line of defense against a bad operator query, script, or deployment. Dolt is online and contains the full history of your database. In a disaster you can use diffs to find a bad query and roll it back. Then you can produce a database patch and apply it to production. You do not need to reinstall from a backup and play the transaction log back to the point of the failure, an extremely time consuming process. -## Change Data Capture +### Change Data Capture [Change Data Capture](https://www.dolthub.com/blog/2023-03-01-change-data-capture/) is a way to add a history of data changes to an existing database. Modern change data capture tools consume replication logs to produce database changes in a consumable stream. Dolt can consume the same logs producing a simpler change data capture solution. From a4e87129cbddd3d639bd7c6dbfc998f4cb5993b5 Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Mon, 17 Aug 2026 11:46:40 -0700 Subject: [PATCH 07/12] Restore the heading hierarchy on the remaining hosted pages Same collapse as notable-features and the use-case pages: 4276c04 demoted each page's section headings from h1 to h2 without demoting their existing h2 subsections. The numbered walkthrough steps in cloning.md and dolthub-as-remote.md, and the workbench/MySQL-client steps in getting-started.md, all rendered as peers of the "Example" section that introduces them rather than steps within it. 22 headings across 3 files, mapped back to their level immediately before 4276c04 plus one for the page h1 that ccc4760 moved into frontmatter. Two headings in cloning.md postdate that baseline (added by a8ff9bf) and were placed by hand: "5. Open a pull request" continues the numbered Example steps, so it joins them at h3; "Branch permissions" is a separate topic that follows the Example, so it stays at h2. Heading lines only, no prose changed, no file skips a level, and no inbound anchor links reference these pages. Co-Authored-By: Claude Opus 5 (1M context) --- .../src/content/products/hosted/cloning.md | 10 +++++----- .../products/hosted/dolthub-as-remote.md | 16 ++++++++-------- .../content/products/hosted/getting-started.md | 18 +++++++++--------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/site/dolt/src/content/products/hosted/cloning.md b/site/dolt/src/content/products/hosted/cloning.md index 606e752..4ad63ed 100644 --- a/site/dolt/src/content/products/hosted/cloning.md +++ b/site/dolt/src/content/products/hosted/cloning.md @@ -33,7 +33,7 @@ analytics query. [Clone](/cli-reference/cli#dolt-clone) makes it easy to get a l copy of your Hosted database with one command. Do whatever potentially destructive or performance-degrading operations on your laptop while respecting your production database. -## 1. Expose remotesapi endpoint +### 1. Expose remotesapi endpoint In order to enable cloning from your Hosted database, first expose the [remotesapi endpoint](/cli-reference/cli#dolt-sql-server). This will set the port for a server @@ -48,7 +48,7 @@ deployment will also work. ![](../../.gitbook/assets/hosted-create-deployment-remotesapi.png) -## 2. Set remote password and run clone command with user flag +### 2. Set remote password and run clone command with user flag To authenticate against it, you have to set a `DOLT_REMOTE_PASSWORD` environment variable and pass along a `--user` flag to the `dolt clone` command. You can find these @@ -92,7 +92,7 @@ cloning https://dolthub-us-housing.dbs.hosted.doltdb.com/us-housing-prices +----------+ ``` -## 3. Sync your local copy with upstream changes +### 3. Sync your local copy with upstream changes Now we can run whatever queries or schema migrations we want without affecting production. If there are updates to the database, easily sync your local copy using [`dolt @@ -107,7 +107,7 @@ commands to your remotesapi endpoint in the same way by passing the `--user` fla % dolt fetch --user "[username]" ``` -## 4. Sync your upstream with changes from local copy +### 4. Sync your upstream with changes from local copy If you make any changes on your local copy, you can push them to your upstream using [`dolt push`](/cli-reference/cli#dolt-push). Push to a feature branch rather than @@ -127,7 +127,7 @@ You can also push straight to `main` if you'd rather skip review, subject to any % dolt push origin --user "[username]" HEAD:main ``` -## 5. Open a pull request +### 5. Open a pull request Hosted Dolt doesn't have forks — that's a DoltHub and DoltLab feature. To contribute a change for review, push a feature branch to the deployment (as in step 4 above) and diff --git a/site/dolt/src/content/products/hosted/dolthub-as-remote.md b/site/dolt/src/content/products/hosted/dolthub-as-remote.md index 5437d3e..179435c 100644 --- a/site/dolt/src/content/products/hosted/dolthub-as-remote.md +++ b/site/dolt/src/content/products/hosted/dolthub-as-remote.md @@ -40,7 +40,7 @@ pull, and fetch from public or private databases. ## Example -## 1. Find a database on DoltHub to clone +### 1. Find a database on DoltHub to clone We have an example user metrics database on DoltHub named `dolthub/user_metrics`. Since we don't want our metrics to be publicly accessible, the database is private. @@ -49,7 +49,7 @@ We want to host this data on Hosted Dolt so that we can use [Google Looker Studio](https://lookerstudio.google.com/) to visualize our data. Learn more about that process [here](https://www.dolthub.com/blog/2023-02-13-dolt-looker/). -## 2. Create a new deployment on Hosted +### 2. Create a new deployment on Hosted Next, I create a [new deployment](https://hosted.doltdb.com/create-deployment) on Hosted and check the `Expose Dolt credentials` check box from the form. @@ -65,14 +65,14 @@ will have access to this key. If I accidentally expose my key or decide I want to remove it, I also have those options there. -## 3. Add public key to DoltHub +### 3. Add public key to DoltHub I click on "Add to DoltHub" to add my public key to DoltHub. This will open my DoltHub [credentials settings page](https://www.dolthub.com/settings/credentials). ![](../../.gitbook/assets/dolthub-credentials-for-hosted.png) -## 4. Connect to Hosted instance and clone DoltHub database +### 4. Connect to Hosted instance and clone DoltHub database Now that I have my credentials set up, I can connect to the Hosted instance using the information in the Connectivity tab and clone my `dolthub/user_metrics` database. @@ -128,7 +128,7 @@ Now I can do whatever I want with my metrics data, including following [these steps](https://www.dolthub.com/blog/2023-02-13-dolt-looker/#create-a-data-source) to connect my Hosted instance to Looker. -## 5. Make changes to database on DoltHub +### 5. Make changes to database on DoltHub Finance reviews our metrics charts and finds a hole in the data suggesting an outage. @@ -139,7 +139,7 @@ editor and create a pull request. They don't even need to know SQL! ![](../../.gitbook/assets/dolthub-spreadsheet-editor-user-metrics.png) -## 6. Pull new DoltHub branch to Hosted instance for testing +### 6. Pull new DoltHub branch to Hosted instance for testing We want to review the new chart from the change before we merge it to `main`. We can pull that branch to our Hosted instance and use it to [create a new branch in @@ -167,7 +167,7 @@ mysql> select * from dolt_diff('main...outage-estimates', 'user_counts'); ``` -## 7. Make a change from Hosted and push it back to DoltHub +### 7. Make a change from Hosted and push it back to DoltHub We are mostly satisfied with the new chart, but I want to make a small update to the metrics for one of the days. I can make the change from Hosted on a branch and push it @@ -207,7 +207,7 @@ I will see the new branch in my database on DoltHub. ![](../../.gitbook/assets/dolthub-outage-updates-branch.png) -## 8. Merge branches into `main` on DoltHub and pull again to Hosted +### 8. Merge branches into `main` on DoltHub and pull again to Hosted From there, our changes are approved and merged into the `main` branch on DoltHub. One more `dolt_pull` will update our `main` branch on Hosted. diff --git a/site/dolt/src/content/products/hosted/getting-started.md b/site/dolt/src/content/products/hosted/getting-started.md index e28df6b..7850e98 100644 --- a/site/dolt/src/content/products/hosted/getting-started.md +++ b/site/dolt/src/content/products/hosted/getting-started.md @@ -55,7 +55,7 @@ There are three ways to read or write from Hosted Dolt. You can: In this blog, we will show off (1) and (2) but we're going to start with the Workbench because it is the easiest to use. -## Start the Workbench +### Start the Workbench Click on the Workbench tab of your deployment. @@ -67,7 +67,7 @@ The workbench has writes off by default. We will turn those on and create a data This should feel like a standard SQL workbench like Tableplus or Datagrip but it's web-based and has some extra Dolt-specific features like a Commit Log and Pull Requests. -## Create Some Tables +### Create Some Tables Now let's create some tables using SQL. We're going to enter the following SQL queries into the query box. You have to run them one at a time. @@ -99,7 +99,7 @@ I finish up by subsequently running the last two create table queries. ![](../../.gitbook/assets/hosted-getting-started/multi-table-diff.png) -## Create a Dolt Commit +### Create a Dolt Commit Now time to use my first Dolt feature! I'm going to create a [Dolt Commit](/concepts/dolt/git/commits). Make a Dolt commit when you want to preserve the state of the Dolt database permanently for future reference. @@ -145,7 +145,7 @@ Taylor can now access the deployment page to get connectivity information. Her m Dolt is a MySQL-compatible database. You can connect any client that can connect to MySQL to it. We're going to use the MySQl client that comes with MySQL in this section to connect to Dolt. -## Install +### Install Head over to the [MySQL Getting Started documentation](https://dev.mysql.com/doc/mysql-getting-started/en/) and install MySQL on your machine. I used [Homebrew](https://brew.sh/) to install MySQL on my Mac. @@ -156,7 +156,7 @@ MySQL comes with a MySQL server called `mysqld` and a MySQL client called `mysql mysql Ver 8.0.29 for macos12.2 on x86_64 (Homebrew) ``` -## Connect +### Connect Now, to connect the mysql client to Dolt, you need the host, port, username, and password from the Connectivity tab. @@ -186,7 +186,7 @@ mysql> This MySQL client is connected to your Hosted Dolt instance. Any changes you make here will be visible to users of the workbench or any other client connected to the Hosted Dolt database. -## Create a branch +### Create a branch So let's be safe and make our changes on a [branch](/concepts/dolt/git/branch). A branch in Dolt is a lightweight way of isolating your changes from the "main" copy of the database. Since our goal is to make a Pull Request in the SQL Workbench, making a branch is necessary as pull requests are done between two branches, in this case "main" and our new branch. @@ -216,7 +216,7 @@ mysql> select active_branch(); Great, Taylor is now on a new branch and can safely make her changes. -## Insert some rows +### Insert some rows This is easy if you know SQL. In the MySQL client on the `inserts` branch, Taylor ran the following SQL to add a few of the early employees here at DoltHub and assign them to teams. @@ -273,7 +273,7 @@ mysql> call dolt_commit('-m', 'inserted early employees'); Let's head back over to the workbench to make a Pull Request. -## Make and Review a Pull Request +### Make and Review a Pull Request The Hosted Dolt workbench supports pull requests and human review of your Hosted Dolt database. Pull requests on Hosted are always opened and merged from the workbench — there are no forks (forks are a [DoltHub](/concepts/dolthub/prs) / [DoltLab](/products/doltlab) feature) and no CLI/SQL surface for opening a PR. The branch you're proposing for review can come from anywhere (the workbench, a MySQL client, a [local clone](/products/hosted/cloning), automation), but the PR itself is created in the web UI. @@ -299,7 +299,7 @@ Finally, she clicks "Create pull request". She now sends me this Pull Request li ![](../../.gitbook/assets/hosted-getting-started/workbench-pr.png) -## Review and Merge +### Review and Merge On the Pull Request Page, I have access to a human readable diff of the changes under review. From cf5c74f0b4c4c6c67a4c9030649aaedfd2bac075 Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Mon, 17 Aug 2026 12:04:51 -0700 Subject: [PATCH 08/12] Restore the heading hierarchy on the server troubleshooting pages Same collapse as the hosted and use-case pages, in both the Dolt and Doltgres copies of reference/sql/server/troubleshooting.md: the diagnostic steps under Basics and the symptoms under Problems rendered as peers of the sections that group them. 17 headings across the two files (9 dolt, 8 doltgres), mapped back to their level immediately before 4276c04 plus one for the page h1 that ccc4760 moved into frontmatter. Every heading matched the baseline, so no manual placement was needed. Submitting Issues correctly stays at h2 between the two grouped sections. Heading lines only; no prose changed, no skipped levels, no inbound anchor links to these pages. Co-Authored-By: Claude Opus 5 (1M context) --- .../reference/sql/server/troubleshooting.md | 18 +++++++++--------- .../reference/sql/server/troubleshooting.md | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/site/dolt/src/content/reference/sql/server/troubleshooting.md b/site/dolt/src/content/reference/sql/server/troubleshooting.md index 4f01afc..ad24ef4 100644 --- a/site/dolt/src/content/reference/sql/server/troubleshooting.md +++ b/site/dolt/src/content/reference/sql/server/troubleshooting.md @@ -7,27 +7,27 @@ Debugging a running Dolt server can be challenging. This document covers the deb ## Basics -## Make sure you are running the latest Dolt version +### Make sure you are running the latest Dolt version Dolt is constantly evolving. We release a new Dolt approximately once a week. Connect to the SQL server and run `select dolt_version()`. Make sure the version matches the latest as seen on the [GitHub releases page](https://github.com/dolthub/dolt/releases). To upgrade the server, download the latest Dolt binary for your platform and replace the Dolt binary on your `PATH` with the downloaded one. Running the install process on most platforms again will do this for you. Restart the Dolt server using `dolt sql-server` to have your running server start using the latest binary. -## Examine your CPU, Memory, and Disk usage +### Examine your CPU, Memory, and Disk usage Dolt consumes CPU, Memory, and Disk. Consuming more of any of these resources than the host has available can lead to degraded performance. Use your system's built in resource monitoring systems to inspect Dolt's usage of these resources. You may need a larger host or additional [read replicas](/sql-reference/server/replication) to support your load. -## Set your log level to DEBUG or TRACE +### Set your log level to DEBUG or TRACE To see queries being run against the server, query results, and query latency set your Dolt log level to `DEBUG` or `TRACE`. This can be done by starting the server like so `dolt sql-server --loglevel=debug` or by setting `log_level: debug` in your `config.yaml`. Your logs should be visible in the shell you started `dolt sql-server` in. -## EXPLAIN PLAN for complex queries +### EXPLAIN PLAN for complex queries Dolt supports the SQL `EXPLAIN PLAN` operation in order for you to see the plan for complex queries. Rearranging your query to perform fewer `JOIN`s or make better use of indexes can help speed up complex queries. Note: `EXPLAIN` currently returns MySQL-consistent but otherwise no-op output. Use `EXPLAIN PLAN` for Dolt formatted plans. -## Compare to MySQL +### Compare to MySQL Dolt strives to be 100% MySQL compatible. If you run a query that works in MySQL but does not work in Dolt, it is a Dolt bug and you should [submit an issue](#submitting-issues). You can dump your Dolt database using [`dolt dump`](/cli-reference/cli#dolt-dump) and import the resulting file into MySQL using `mysql < dump.sql`. The test the query you think should work using any MySQL client. @@ -39,7 +39,7 @@ If you run into any issues requiring engineering attention, please submit a [Git Dolt operational issues usually manifest as slow SQL queries. In rare occasions, Dolt may consume more of your system's resources than you expect. In these cases, this document has some recommendations. -## Server Consuming Disk +### Server Consuming Disk Dolt creates disk garbage on write. As of Dolt 1.75, automatic garbage collection is on by default. Thus, you should not experience disk garbage accumulation under normal operation for newer versions of Dolt. @@ -50,7 +50,7 @@ Another potential cause is a commit-heavy workflow that uses a database design t * Using primary keys with random values. Inserts into indexes with random values guarantees that edits will occur all throughout the index instead of being clustered around the same key space. This results in a rewrite of the Prolly Tree thereby increasing storage disproportionately to the delta of the changes. * Adding a column to a table. A new column forks the storage of the table resulting in a loss of structural sharing. Dolt is row major and builds chunks for each primary key, row values pair. The row values encodes the schema length so every row now requires a new chunk. -## Server Consuming Memory +### Server Consuming Memory Serving Dolt databases requires a fair amount of memory. As a general rule, we recommend a minimum of 2GB available RAM for any production use case. Larger databases or heavier workloads should start @@ -70,7 +70,7 @@ then frees the memory upon restart, you have discoverd a memory leak. Again, ple issue](https://github.com/dolthub/dolt/issues). Memory leaks should be rare and we treat memory leak fixes as high priority. -## Server Consuming CPU +### Server Consuming CPU Under too much concurrent load, Dolt may consume all the CPU on a host. This is likely caused by too much read concurrency. In this case, create more [read replicas](/sql-reference/server/replication) and load balance @@ -79,7 +79,7 @@ your reads among your replicas. If you discover a query consuming all of your CPU, please submit a [GitHub Issue](https://github.com/dolthub/dolt/issues). On rare occasions, this could be a Dolt bug. -## Too much write concurrency +### Too much write concurrency Currently, Dolt is not a high-throughput database for writes. The current transaction model serializes all writes, which means that after a certain threshold of writer concurrency, you'll diff --git a/site/doltgres/src/content/reference/sql/server/troubleshooting.md b/site/doltgres/src/content/reference/sql/server/troubleshooting.md index 94bfc92..f75253a 100644 --- a/site/doltgres/src/content/reference/sql/server/troubleshooting.md +++ b/site/doltgres/src/content/reference/sql/server/troubleshooting.md @@ -6,7 +6,7 @@ Debugging a running Doltgres server can be challenging. This document covers the ## Basics -## Make sure you are running the latest Doltgres version +### Make sure you are running the latest Doltgres version Doltgres is constantly evolving. We release a new Doltgres approximately once a week. Connect to the SQL server and run `select dolt_version()`. Make sure the version matches the latest as seen on the @@ -14,15 +14,15 @@ SQL server and run `select dolt_version()`. Make sure the version matches the la To upgrade the server, download the latest Doltgres binary for your platform and replace the Doltgres binary on your `PATH` with the downloaded one. Running the install process on most platforms again will do this for you. Restart the Doltgres server using `doltgres` to have your running server start using the latest binary. -## Examine your CPU, Memory, and Disk usage +### Examine your CPU, Memory, and Disk usage Doltgres consumes CPU, Memory, and Disk. Consuming more of any of these resources than the host has available can lead to degraded performance. Use your system's built in resource monitoring systems to inspect Doltgres's usage of these resources. You may need a larger host or additional [read replicas](/reference/server/replication) to support your load. -## Set your log level to DEBUG or TRACE +### Set your log level to DEBUG or TRACE To see queries being run against the server, query results, and query latency set your Doltgres log level to `DEBUG` or `TRACE`. This can be done by setting `log_level: debug` in your `config.yaml`. Your logs should be visible in the shell you started `doltgres` in. -## Compare to Postgres +### Compare to Postgres Doltgres strives to be 100% Postgres compatible. If you run a query that works in Postgres but does not work in Doltgres, it is a Doltgres bug and you should [submit an issue](#submitting-issues). You can dump your Doltgres database using the `pg_dump` tool and import the resulting file into PostgreSQL. Then test the query you think should work against both servers using any Postgres client and compare the results. @@ -34,7 +34,7 @@ If you run into any issues requiring engineering attention, please submit a [Git Doltgres operational issues usually manifest as slow SQL queries. In rare occasions, Doltgres may consume more of your system's resources than you expect. In these cases, this document has some recommendations. -## Server Consuming Disk +### Server Consuming Disk Doltgres creates disk garbage on write. This can sometimes become a substantial portion of the disk Doltgres is consuming. Doltgres ships with a garbage collection function. Running the garbage collection function can free disk. @@ -47,7 +47,7 @@ Another potential cause is a commit-heavy workflow that uses a database design t - Using primary keys with random values. Inserts into indexes with random values guarantees that edits will occur all throughout the index instead of being clustered around the same key space. This results in a rewrite of the prolly tree thereby increasing storage disproportionately to the delta of the changes. - Adding a column to a table. A new column forks the storage of the table resulting in a loss of structural sharing. Doltgres is row major and builds chunks for each primary key, row values pair. The row values encodes the schema length so every row now requires a new chunk. -## Server Consuming Memory +### Server Consuming Memory Serving Doltgres databases requires a fair amount of memory. As a general rule, we recommend a minimum of 2GB available RAM for any production use case. Larger databases or heavier workloads should start @@ -67,7 +67,7 @@ then frees the memory upon restart, you have discoverd a memory leak. Again, ple issue](https://github.com/dolthub/doltgresql/issues). Memory leaks should be rare and we treat memory leak fixes as high priority. -## Server Consuming CPU +### Server Consuming CPU Under too much concurrent load, Doltgres may consume all the CPU on a host. This is likely caused by too much read concurrency. In this case, create more [read replicas](/reference/server/replication) and load balance @@ -76,7 +76,7 @@ your reads among your replicas. If you discover a query consuming all of your CPU, please submit a [GitHub Issue](https://github.com/dolthub/doltgresql/issues). On rare occasions, this could be a Doltgres bug. -## Too much write concurrency +### Too much write concurrency Currently, Doltgres is not a high-throughput database for writes. The current transaction model serializes all writes, which means that after a certain threshold of writer concurrency, you'll From e5c400c8a4515852bae2993b94c3ef608525fd42 Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Mon, 17 Aug 2026 12:25:05 -0700 Subject: [PATCH 09/12] Drop the duplicate heading on expressions-functions-operators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The page opened with an empty "## Expressions, Functions, Operators" heading restating its own title. It was a near-duplicate h1 in the source ("Functions, Operators" vs the title's "Functions, and Operators"); 4276c04 demoted it to h2 and ccc4760 then moved the real title into frontmatter, leaving the copy behind with no content under it. Removing it is the right fix here rather than re-leveling. The remaining headings — Statements, Clauses, Table expressions, and the rest — were always siblings, so they stay at h2 and the page needs no other change. The same pattern covers the 44 DoltLab release notes, where nesting sections under the duplicate would have been worse than leaving them flat. The one inbound link to this page targets #window-functions, which is unaffected and still resolves. Co-Authored-By: Claude Opus 5 (1M context) --- .../sql/sql-support/expressions-functions-operators.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/site/dolt/src/content/reference/sql/sql-support/expressions-functions-operators.md b/site/dolt/src/content/reference/sql/sql-support/expressions-functions-operators.md index e391f5a..d40d970 100644 --- a/site/dolt/src/content/reference/sql/sql-support/expressions-functions-operators.md +++ b/site/dolt/src/content/reference/sql/sql-support/expressions-functions-operators.md @@ -3,8 +3,6 @@ title: "Expressions, Functions, and Operators" description: Supported scalar functions, operators, and expressions. --- -## Expressions, Functions, Operators - ## Statements | Component | Supported | Notes and limitations | From 225618d848d6596df1be76102010fa384fd429f6 Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Mon, 17 Aug 2026 12:27:42 -0700 Subject: [PATCH 10/12] Restore the heading hierarchy on the CI overview Same collapse as the other pages: "Dolt CI Commands" and "Saved Query" rendered as peers of the sections they belong to rather than parts of them. 2 headings, mapped back to their level immediately before 4276c04 plus one for the page h1 that ccc4760 moved into frontmatter. Dolt CI Commands nests under "CI starts with Dolt" and Saved Query under "Steps"; Workflows, Events, and Jobs were always siblings and stay at h2. Heading lines only, no skipped levels, and nothing links to this page's anchors. Co-Authored-By: Claude Opus 5 (1M context) --- .../content/products/dolthub/continuous-integration/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/dolt/src/content/products/dolthub/continuous-integration/README.md b/site/dolt/src/content/products/dolthub/continuous-integration/README.md index 9f231cc..7a67379 100644 --- a/site/dolt/src/content/products/dolthub/continuous-integration/README.md +++ b/site/dolt/src/content/products/dolthub/continuous-integration/README.md @@ -18,7 +18,7 @@ The following sections will introduce you to how CI works with Dolt, DoltHub and CI configuration for a DoltHub or DoltLab database is stored in the database itself. At the time of this writing, in order to add CI configuration to a DoltHub or DoltLab database, you will need to have a local Dolt client version >= [v1.45.3](https://github.com/dolthub/dolt/releases/tag/v1.45.3) and will have to clone a copy of the the database. In order to configure CI on the database, you will use Dolt's CI CLI commands. -## Dolt CI Commands +### Dolt CI Commands The primary interface for creating and editing CI configuration in a Dolt database is via the `dolt ci` CLI command. These commands aim to simplify CI configuration in Dolt, so that users do not need to manually interact with the underlying CI tables directly. @@ -57,7 +57,7 @@ A workflow Job is made up of a series of Steps. A step, in its current form, is For more information on Steps, please see the [workflow reference](/products/dolthub/continuous-integration/reference). -## Saved Query +### Saved Query A [Saved Query](/sql-reference/version-control/saved-queries) is a SQL query that is stored and versioned in a Dolt database. For the purpose of DoltHub and DoltLab CI, this allows users to write a SQL query that will be executed on command at a later time, during a CI run. From 6962f6d535280af384e2cea932873866d9d64c98 Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Mon, 17 Aug 2026 12:52:44 -0700 Subject: [PATCH 11/12] Restore the heading hierarchy on storage-engine and garbage-collection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit storage-engine.md: B-Trees, Prolly Trees, and Comparison are the search-tree types "The Database Backbone: Search Trees" introduces, not peers of it. garbage-collection.md (dolt): Offline and "Online, with Automatic GC disabled" are the two ways "How to run garbage collection manually" describes — that section's own text introduces both. 5 headings, mapped back to their pre-4276c04 level plus one. Heading lines only, no skipped levels. The two inbound anchor links to these pages (#commit-graph, #automatic-gc) point at headings that stay at h2. Doltgres's garbage-collection.md is deliberately left alone. The mechanical mapping would nest its "Online" section under "How garbage is created", which is where the original had it — but that section is about running GC via select dolt_gc(), not about how garbage accumulates. The original nesting was an authoring mistake, and its current flat structure is already correct. Co-Authored-By: Claude Opus 5 (1M context) --- site/dolt/src/content/architecture/storage-engine.md | 6 +++--- .../src/content/reference/sql/server/garbage-collection.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/site/dolt/src/content/architecture/storage-engine.md b/site/dolt/src/content/architecture/storage-engine.md index 111feb7..05f743e 100644 --- a/site/dolt/src/content/architecture/storage-engine.md +++ b/site/dolt/src/content/architecture/storage-engine.md @@ -54,7 +54,7 @@ Databases are built on top of [Search Trees](https://en.wikipedia.org/wiki/Searc Dolt is built on a novel Search Tree, closely related to a B-tree, called a Probabilistic B-Tree, or Prolly Tree for short. As far as we can tell, Prolly Trees were [invented by the Noms team specifically for database version control](https://github.com/attic-labs/noms/blob/master/doc/intro) and they also coined the term. -## B-Trees +### B-Trees Most SQL databases you are familiar with, like [Postgres](https://www.postgresql.org/) or [MySQL](https://www.mysql.com/), are built on [B-tree](https://www.dolthub.com/blog/2020-04-01-how-dolt-stores-table-data/#b-tree-review) storage. Tables are represented as a map of primary keys to values and the keys of that map are stored in a B-tree. Values are stored in the leaf nodes. @@ -66,7 +66,7 @@ However, finding the differences between two B-trees requires scanning both tree Also, writes to B-trees are not history independent, the order of the writes internally changes the structure of the tree. Thus, storage cannot be easily shared between two versions of the same tree. -## Prolly Trees +### Prolly Trees A Prolly Tree, or Probabilistic B-tree, is a content-addressed B-tree. @@ -82,7 +82,7 @@ Moreover, sections of the tree that share the same root hash can share storage b Prolly trees are described in more detail [here](/architecture/storage-engine/prolly-tree). -## Comparison +### Comparison [The Noms documentation](https://github.com/attic-labs/noms/blob/master/doc/intro#some-properties-of-prolly-trees) provides the following useful algorithmic, big O() comparison of B-trees and Prolly Trees: diff --git a/site/dolt/src/content/reference/sql/server/garbage-collection.md b/site/dolt/src/content/reference/sql/server/garbage-collection.md index e3166cc..67e5efa 100644 --- a/site/dolt/src/content/reference/sql/server/garbage-collection.md +++ b/site/dolt/src/content/reference/sql/server/garbage-collection.md @@ -37,11 +37,11 @@ $ dolt sql --disable-auto-gc Garbage collection can be run offline using [`dolt gc`](/cli-reference/cli#dolt-gc) or online using [`call dolt_gc()`](/sql-reference/version-control/dolt-sql-procedures#dolt_gc). -## Offline +### Offline If you have access to the server where your Dolt database is located and a Dolt sql-server is not running, navigate to the directory your database is stored in and run `dolt gc`. This will cycle through all the needed chunks in your database and delete those that are unnecessary. This process is CPU and memory intensive. -## Online, with Automatic GC disabled +### Online, with Automatic GC disabled If you have disabled Automatic GC, you can run garbage collection on your running SQL server using [`call dolt_gc`](/sql-reference/version-control/dolt-sql-procedures#dolt_gc) through any connected client. To prevent concurrent writes potentially referencing garbage collected chunks, running From 6e8c09eb3f856f5b22f325067717c4ea74aacb7c Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Mon, 17 Aug 2026 12:52:44 -0700 Subject: [PATCH 12/12] Restore the heading hierarchy on the DoltLab guides MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getting-started.md: "Make sure Docker works" belongs to the download step, and the Basic Configuration and Adding Additional Functionality sections each own the options listed beneath them. pre-installer-administrator-guide.md: the three backup/restore methods sit under "Backup and restore volumes", and the two setup steps under "Run DoltLab on Hosted Dolt". 16 headings across the two files, mapped back to their pre-4276c04 level plus one. Heading lines only, no skipped levels. The inbound anchor links from aws/gcp/azure and start-doltlab-pre-installer all target headings that stay at h2. The 44 release notes are left alone — their extra heading is a restatement of the page title rather than a lost section, so they need a deletion rather than this re-leveling. Co-Authored-By: Claude Opus 5 (1M context) --- .../getting-started/getting-started.md | 22 +++++++++---------- .../pre-installer-administrator-guide.md | 10 ++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/site/doltlab/src/content/introduction/getting-started/getting-started.md b/site/doltlab/src/content/introduction/getting-started/getting-started.md index 399ce21..dc61ef5 100644 --- a/site/doltlab/src/content/introduction/getting-started/getting-started.md +++ b/site/doltlab/src/content/introduction/getting-started/getting-started.md @@ -80,7 +80,7 @@ ubuntu@ip-10-2-0-24:~/doltlab$ ./ubuntu_install.sh There are a few dependencies. This will take a couple minutes. -## Make sure Docker works +### Make sure Docker works DoltLab uses Docker to run all of its services. After the bootstrap script is done, make sure Docker works with sudo by running: @@ -197,7 +197,7 @@ The DoltLab instance ships with a default user configured called `admin`. It has ## Basic Configuration -## Single user +### Single user A configured email server is required to create new users. If you try to create a user, you'll get an error that looks like this: @@ -207,7 +207,7 @@ So, you only have a single admin user to play with for now. > UPDATE: as of DoltLab v2.2.0, an email server is no longer required to create new users on a DoltLab instance. New users will be able to create accounts on an a DoltLab instance openly, unless [account whitelisting](/guides/basic#prevent-unauthorized-user-account-creation) has been enabled by the administrator. Additionally, email server configuration is now an exclusive [DoltLab Enterprise feature](/guides/enterprise#connect-doltlab-to-an-smtp-server). -## Create/Modify databases +### Create/Modify databases You can build whatever database you can imagine using the web user interface. The built in SQL workbench can be used to create and edit tables. You can import CSVs. You can edit a table using the spreadsheet editor. You can make branches and Pull Requests. @@ -215,7 +215,7 @@ Here's a simple test database I created using SQL. ![](../../.gitbook/assets/getting-started/doltlab-admin-db.png) -## Clone Databases +### Clone Databases DoltLab is a remote so you can clone databases from it. The remote API is exposed on port 50051. I made my test database public so I don't need any permissions to clone it. @@ -236,7 +236,7 @@ $ dolt sql -q "select * from t" I now have the test database I created on my DoltLab locally. -## Push Databases +### Push Databases To push databases you need your Dolt client authenticated against your DoltLab instance. You can run a fresh `dolt login` against DoltLab using something like this: @@ -279,26 +279,26 @@ You're now ready to try out all the Dolt and DoltLab experiences like Pull Reque ## Adding Additional Functionality -## Create new users +### Create new users As discussed in [the single user section](#single-user), creating users in DoltLab <= `v2.1.6` requires a working SMTP server to send emails. However, DoltLab >= `v2.2.0` does not require an SMTP server connection in order for new users to create accounts on your DoltLab instance. In this newer version, an SMTP server connection can only be made with DoltLab Enterprise. For more information, consult [this guide to set up an email server for DoltLab](/guides/enterprise#set-up-a-smtp-server-using-any-gmail-address). -## Receive Email Notifications +### Receive Email Notifications DoltLab Enterprise sends emails for password resets, pull request and issue status, and a few other use cases. Obviously, these also won't work without a running email server. Consult [this guide to set up an email server for DoltLab](/guides/enterprise#set-up-a-smtp-server-using-any-gmail-address). -## HTTPS +### HTTPS Your DoltLab is currently set up to only use HTTP which is fairly insecure. If it's running on your internal network and you have other threat mitigations, this may be OK. But having it sit on the public internet on AWS without HTTPS is probably not what you want. Native HTTPS support is available in DoltLab Enterprise. [Learn how to set up HTTPS on your DoltLab here](/guides/enterprise#serve-doltlab-over-https-natively). -## Custom URL +### Custom URL Right now, you're stuck hitting the IP address of your host. In order to use a custom URL on the internet, you need a static IP for your EC2 host and a domain name. If your host is running on an internal network, you may need to follow a different process. -## Custom Logo/Colors +### Custom Logo/Colors Yay! Our first DoltLab Enterprise feature. If you would like a custom logo and color scheme for your DoltLab instance, you are going to need [DoltLab Enterprise](/guides/enterprise). DoltLab Enterprise is $5,000/month for unlimited users. It comes with Enterprise Support for Dolt as well. Among [other features](/guides/enterprise), DoltLab Enterprise allows you to customize the look and feel of your DoltLab instance. Learn how to [configure your DoltLab as Enterprise](/guides/enterprise) and [set up your custom look and feel here](/guides/enterprise#use-custom-logo). -## Scalability +### Scalability Lastly, your DoltLab is running on a single host. All the components will scale to the size of that host including storing all the databases that are created. Storage and compute requirements can get big quickly. In order to break your DoltLab up into multiple instances and use cloud storage to store your databases, you need [DoltLab Enterprise](/guides/enterprise). Learn [how to set up Enterprise here](/guides/enterprise). diff --git a/site/doltlab/src/content/older/pre-installer-administrator-guide.md b/site/doltlab/src/content/older/pre-installer-administrator-guide.md index 0db1df3..2ee59ec 100644 --- a/site/doltlab/src/content/older/pre-installer-administrator-guide.md +++ b/site/doltlab/src/content/older/pre-installer-administrator-guide.md @@ -29,7 +29,7 @@ DoltLab persists all data to local disk using Docker volumes. To backup or resto DoltLab <= `v0.8.4` uses PostgreSQL as its database and DoltLab `v1.0.0`+ uses Dolt. To backup the PostgreSQL server we recommend dumping the database with `pg_dump` and restoring the database from the dump using `psql`. To backup the Dolt server we recommend using Docker's volume backup and restore process, or Dolt's built-in backup and restore features. -## Backing up and restoring remote data, user uploaded data, and Dolt server data with Docker +### Backing up and restoring remote data, user uploaded data, and Dolt server data with Docker To backup DoltLab's remote data, the database data for all database on a given DoltLab instance, leave DoltLab's services up and run: @@ -141,7 +141,7 @@ docker run --rm --volumes-from doltlab_doltlabdb_1 -v $(pwd):/backup ubuntu bash You can now restart DoltLab, and should see all data restored from the `tar` files. -## Backing up and restoring PostgreSQL data +### Backing up and restoring PostgreSQL data For DoltLab versions <= `v0.8.4`, to backup data from DoltLab's postgres server, we recommend executing a data dump with `pg_dump`. To do so, keep DoltLab's services up and run: @@ -197,7 +197,7 @@ SET session_replication_role = replica; docker run --rm --network doltlab_doltlab -e PGPASSWORD= -v $(pwd):/doltlab-db-dumps postgres:13-bullseye bash -c "psql --host=doltlab_doltlabdb_1 --port=5432 --username=dolthubadmin dolthubapi < /doltlab-db-dumps/postgres-dump.sql" ``` -## Backing up and restoring the Dolt server with `dolt backup` +### Backing up and restoring the Dolt server with `dolt backup` DoltLab `v1.0.0`+ uses Dolt as its database. To back up the Dolt server of DoltLab using Dolt's built-in backup and restore features, keep DoltLab's services up and open a connection to the database. The quickest way to do this is with the `./shell-db.sh` script included with DoltLab: @@ -592,7 +592,7 @@ Starting with DoltLab `v1.0.0`, DoltLab can be configured to use a [Hosted Dolt] To configure a DoltLab to use a Hosted Dolt, follow the steps below as we create a sample DoltLab Hosted Dolt instance called `my-doltlab-db-1`. -## Create a Hosted Dolt deployment +### Create a Hosted Dolt deployment To begin, you'll need to create a Hosted Dolt deployment that your DoltLab instance will connect to. We've created a [video tutorial](https://www.dolthub.com/blog/2022-05-20-hosted-dolt-howto/) for how to create your first Hosted Dolt deployment, but briefly, you'll need to create an account on [hosted.doltdb.com](https://hosted.doltdb.com) and then click the "Create Deployment" button. @@ -639,7 +639,7 @@ You can do this by running these statements from the Hosted workbench SQL consol This instance is now ready for a DoltLab connection. -## Edit DoltLab's Docker Compose file +### Edit DoltLab's Docker Compose file To connect DoltLab to `my-doltlab-db-1`, ensure that your DoltLab instance is stopped, and remove references to `doltlabdb` in DoltLab's `docker-compose.yaml` file.