kds-public-api
Corpo da especificação
KDS Public API (v1)
Normative contract for the JSON + text endpoints under
https://kds.koder.dev/api/v1/ and the AI-agent discovery surfaces
at https://kds.koder.dev/llms.txt + /llms-full.txt.
Consumers today:
engines/sdk/koder-design-lsp(editor hover + autocomplete fromtokens.json, 24h TTL cache)- Figma sync (per-deploy snapshot of
tokens.json) - AI agents (Claude Code, Cursor, Copilot, Gemini) via
llms.txt+llms-full.txt - Internal monitors / scripts
- Future: MCP server (
tools/design-gen#117)
This spec exists so consumers can pin against a known contract and the generator can change implementation without silently breaking them.
Status: v0.1.0 Draft. Move to Ratified when: (a) every endpoint below has a passing contract test in
tools/design-gen/tests/regression/, AND (b) at least one external consumer (LSP) is wired to validate against the JSON schemas before parsing.
R1 — Endpoint inventory
The canonical surface, as of v0.1.0:
| Path | Method | Content-Type | Purpose |
|---|---|---|---|
/api/v1/index.json | GET | application/json | Endpoint catalog discovery (10+ endpoints) |
/api/v1/specs.json | GET | application/json | Catalog of every published spec (slug + locale URLs + kind + summary) |
/api/v1/specs/<slug>.json | GET | application/json | Per-spec payload (full frontmatter + body markdown + cross-refs) |
/api/v1/specs/openapi.yaml | GET | text/yaml | OpenAPI 3.1 description of /api/v1/specs/* |
/api/v1/tokens.json | GET | application/json | LSP-consumable token manifest (Verge tokens + presets) |
/<locale>/search-index.json | GET | application/json | Per-locale full-text search index |
/<locale>/mcp/descriptor.json | GET | application/json | MCP resource descriptor (future MCP server input — #117) |
/<locale>/llms.txt | GET | text/plain; charset=utf-8 | llmstxt.org-compliant AI agent discovery index |
/<locale>/llms-full.txt | GET | text/plain; charset=utf-8 | Full markdown corpus for RAG ingestion |
/llms.txt | GET | text/plain; charset=utf-8 | Locale-negotiated alias of /<default-locale>/llms.txt |
/sitemap.xml | GET | application/xml | Crawler discovery (env-aware noindex per policies/environments.kmd) |
/feed.xml | GET | application/rss+xml | RSS feed of versioned releases |
Locales served today: en-US, pt-BR, es-ES.
Future endpoints (planned, not part of v1):
/api/v1/index/<kind>.json(per-kind subindex —#110)/mcp(MCP HTTP+SSE server —#117)
R2 — Response shapes (JSON schema fragments)
/api/v1/index.json
{
"endpoints": [
{
"url": "https://kds.koder.dev/api/v1/specs.json",
"kind": "catalog",
"description": "Full spec catalog with cross-refs"
},
{
"url": "https://kds.koder.dev/api/v1/tokens.json",
"kind": "tokens-manifest",
"description": "Verge + UI-style token manifest"
}
],
"_meta": {
"version": "v1",
"generated_at": "2026-05-24T18:00:00Z",
"commit_sha": "abc123"
}
}
/api/v1/specs.json
{
"specs": [
{
"slug": "verge",
"kind": "theme",
"name": "Verge (v0 — Adwaita-based)",
"summary": "Canonical visual language of the Koder Design System",
"locales": {
"en-US": "https://kds.koder.dev/en-US/themes/verge/",
"pt-BR": "https://kds.koder.dev/pt-BR/themes/verge/",
"es-ES": "https://kds.koder.dev/es-ES/themes/verge/"
},
"source": "meta/docs/stack/specs/themes/verge.kmd"
}
]
}
/api/v1/tokens.json
{
"verge": {
"color": { "primary": "#3584e4", "...": "..." },
"spacing": { "1": "4px", "2": "8px", "...": "..." },
"radius": { "sm": "4px", "md": "8px", "...": "..." }
},
"presets": [ { "slug": "material3", "tokens": {...} } ]
}
Schemas above are descriptive baselines. The implementation may add fields; consumers MUST tolerate unknown fields.
R3 — Versioning policy
/api/v1/ is the major version. Backwards-compatible changes ship
in place; breaking changes trigger a new major path prefix
(/api/v2/).
Backwards-compatible:
- Adding new fields to existing response shapes
- Adding new endpoints under
/api/v1/ - Adding new spec kinds to existing catalog responses
- Adding new locales (existing entries gain
locales.<new>keys) - Loosening field types (string → string | null)
Breaking (require /api/v2/):
- Removing or renaming any field
- Changing the type of a field (string → number, scalar → object)
- Removing an endpoint
- Removing a locale (consumers may have pinned URLs)
- Changing the semantics of a value within the same field type
Major-version bumps SHOULD ship with at least 90 days of /api/v1/
overlap so consumers can migrate. Removal of /api/v1/ requires a
ticketed sunset announcement.
R4 — Compatibility guarantees
Consumers MAY rely on these:
- G1 — JSON responses are valid UTF-8 with no BOM
- G2 — Top-level keys named in this spec WILL be present in every response (no conditional fields at the top level)
- G3 — URLs in responses are always absolute (
https://kds.koder.dev/...), never relative or protocol-relative - G4 — Slugs are stable across renames; if a spec slug changes,
the old slug remains addressable via a
301redirect for the remainder of/api/v1/ - G5 — Locale codes are BCP 47 (
en-US,pt-BR,es-ES); locale in URL ALWAYS uses uppercase region code - G6 —
_metaobject (when present) is informational; consumers MUST NOT fail if absent (it's only emitted when build-time env vars are set — see R5)
Consumers MUST NOT rely on these:
- N1 — Order of array elements (sort client-side if order matters)
- N2 — Whitespace / formatting of JSON payloads (may be minified)
- N3 — Specific
_metafield shape beyondcommit_sha+generated_at - N4 — That
/api/v1/index.jsonwill list EVERY endpoint forever (the catalog grows; consumers should re-fetch on cache miss, not hard-code endpoint URLs)
R5 — Caching contract (cross-link #108)
The generator-emitted Cache-Control headers (set in
tools/design-gen/deploy/prd.kds.koder.dev.site.toml):
| Path | Cache-Control | Rationale |
|---|---|---|
/api/v1/tokens.json | public, max-age=3600, stale-while-revalidate=86400 | Tokens shift slowly; 1h fresh + 24h SWR |
/api/v1/specs.json, /api/v1/index.json | public, max-age=300, stale-while-revalidate=3600 | Catalog — 5min fresh + 1h SWR |
/<locale>/llms.txt, /<locale>/llms-full.txt | public, max-age=3600, stale-while-revalidate=86400 | Same as tokens — AI agents |
/<locale>/search-index.json | public, max-age=300, stale-while-revalidate=3600 | Same as catalog |
/<locale>/mcp/descriptor.json | public, max-age=3600, stale-while-revalidate=86400 | Same as tokens |
Consumers SHOULD honor stale-while-revalidate for resilience and
to reduce origin pressure during deploy windows. ETag + Last-Modified
SHOULD be present (Jet emits these automatically for static files).
R6 — Discovery convention
Discovery starts at /api/v1/index.json — every other endpoint
under /api/v1/ MUST appear there. Future addition:
/.well-known/kds.json MAY ship (RFC 8615) for cross-origin
discovery without a path guess.
llms.txt-style endpoints follow the llmstxt.org convention:
/llms.txt— short index, prepended with locale-negotiation hint/<locale>/llms.txt— per-locale (canonical)/<locale>/llms-full.txt— full corpus per locale
Search:
/<locale>/search-index.json— per-locale full-text index; client-side runtime filters
R7 — Freshness fingerprint (cross-link #112)
When build-time env vars KDS_COMMIT_SHA + KDS_GENERATED_AT are
set, the generator emits a _meta block in JSON responses + a
header comment in llms.txt:
> <!-- kds-fingerprint commit=abc123 generated=2026-05-24T18:00:00Z -->
Plus a standalone dist/.deploy-fingerprint.json:
{
"commit_sha": "abc123",
"generated_at": "2026-05-24T18:00:00Z",
"tool": "design-gen",
"version": "v0.1.0"
}
A freshness-probe tool (dev/koder-tools/cmd/koder-kds-freshness-probe/)
runs on a cron schedule comparing live kds.koder.dev/llms.txt
fingerprint against the latest commit on master touching
tools/design-gen/ or meta/docs/stack/specs/. Drift > 24h opens
an alert.
R8 — Body content trust model
Spec bodies in /api/v1/specs/<slug>.json (and the corresponding
<body> HTML rendered at /<locale>/<kind>/<slug>.html) are
first-party PR-gated content, NOT arbitrary user input.
R8.1 — Markdown is rendered through Goldmark with
html.WithUnsafe() enabled (tools/design-gen/internal/kinds/pattern.go).
Inline raw HTML in spec bodies — <details>, <style> scoped to
the page, inline <script> for interactive playgrounds — renders
verbatim into both the published HTML and the API payload body.
R8.2 — Consumers of /api/v1/specs/*.json MAY render body /
body_html fields without additional sanitization. Doing so is
safe ONLY because the trust boundary is "authored by a Koder Stack
contributor with merge rights to meta/docs/stack/", and that
boundary is enforced by the same PR + review workflow that gates
any other code change in the monorepo.
R8.3 — Consumers that redistribute spec bodies into contexts where the original trust boundary does not extend (e.g. embedding a spec body inside a multi-tenant SaaS dashboard where end-users can deep-link KDS content) MUST sanitize before rendering. The KDS API does not pre-sanitize; the integration carries the responsibility.
R8.4 — Cross-link: policies/security.kmd for the broader Stack
posture on trusted vs untrusted content boundaries.
T1 — Endpoint reachability test
/api/v1/index.json resolves with HTTP 200 + application/json
Content-Type for every published endpoint.
curl -sf https://kds.koder.dev/api/v1/index.json | jq -e '.endpoints | length > 5'
T2 — Schema validity test
/api/v1/specs.json parses as valid JSON; every entry has slug,
kind, name, summary, locales, source fields per R2.
T3 — URL absolutization test
Every URL in /api/v1/specs.json .locales values starts with
https://kds.koder.dev/ (G3 enforcement).
T4 — Locale parity test
Every spec entry has locales.en-US; entries with locales.pt-BR /
locales.es-ES resolve to live pages.
T5 — Cache headers test (post-#108)
curl -sI https://kds.koder.dev/api/v1/tokens.json returns
cache-control: public, max-age=3600, stale-while-revalidate=86400.
T6 — Slug stability test
Specs in done/ backlog (renamed historically) still resolve via
slug redirects (G4 enforcement). Requires a test fixture mapping
old slug → 301 → new slug.
N1 — Removed endpoint returns 404
A removed endpoint MUST return HTTP 404 (not 200 with empty body).
N2 — Bad locale returns 404
/api/v1/specs/<slug>/?locale=xx-XX returns 404, not falls back to
default locale (consumer must request a supported locale).
N3 — Malformed _meta is tolerated
A future build that emits a NEW _meta field MUST NOT break
consumers that parsed v0.1.0 (compatibility test runs against
historical fixture payloads).
Tooling
- Generator:
tools/design-gen/internal/api/(api.go, index.go, mcp.go) +internal/llmstxt/llmstxt.go - Token emitter:
tools/design-gen/internal/tokens/extract.go - Contract tests:
tools/design-gen/tests/regression/api_*_test.go(TBD per T1-T6) - Freshness probe:
dev/koder-tools/cmd/koder-kds-freshness-probe/(per#112)
Não-escopo
- Write API — KDS is read-only; specs change via PR against
meta/docs/stack/specs/ - gRPC / GraphQL alternatives — static JSON is the contract
- Per-user authentication — KDS is anonymous read-only
- Webhooks / push notifications — consumers poll with the SWR Cache-Control hint
Related work
tools/design-gen#108— Cache-Control headers (R5)tools/design-gen#110— Per-kind index splittools/design-gen#112— Freshness fingerprint + probe (R7)tools/design-gen#117— MCP server (alternative live query surface — separate from/api/v1/)meta/docs/stack/registries/token-consumers.md— every consumer pinning against this API
Referências
tools/design-gen/internal/api/api.gotools/design-gen/internal/api/index.gotools/design-gen/internal/api/mcp.gotools/design-gen/internal/llmstxt/llmstxt.gometa/docs/stack/registries/token-consumers.mdtools/design-gen/backlog/pending/108-api-v1-cache-control.kmdtools/design-gen/backlog/pending/110-api-index-filter-by-kind.kmdtools/design-gen/backlog/pending/117-mcp-server-for-kds.kmd