Semver on every document
Markdown, OpenAPI, JSON or plain text — each is versioned x.y.z. “Which version did we build against?” is a question with a permanent answer, and ?v=2.1.3 keeps returning 2.1.3.
Features
Docxoft treats a document the way your build treats a package: versioned, immutable, and depended on by name. Here is everything it does, grouped by the question it answers.
Docs as contracts
A wiki page has one state. A document other teams build against has as many states as there are things built against it, so every version here is numbered, immutable and explained.
Markdown, OpenAPI, JSON or plain text — each is versioned x.y.z. “Which version did we build against?” is a question with a permanent answer, and ?v=2.1.3 keeps returning 2.1.3.
Publishing writes a new object under a key built from ids, conditionally, so a retry can never overwrite a body that already exists. Old versions stay byte-for-byte retrievable.
Not optional and not generated from commit messages afterwards. A consumer asking “2.3.0 to 2.5.1, is this safe?” gets prose written by whoever made the change.
Both bodies are parsed and compared by operation, parameter, request body and response — never line by line. Reordering paths is nothing; removing an endpoint is a major.
Ask for a patch when the diff implies a major and the server answers 409 bump-too-low, naming the change. force: true overrides it and is audited as an override.
The bytes you wrote are the bytes an agent reads, rendered for people at read time. No lossy import step, no proprietary block format to escape from later.
Private by default
Nothing in Docxoft is public. Every read is authenticated and filtered by a predicate that lives in the query layer, so a handler cannot forget it.
cds_live_… token carries read, write or publish, is shown once at creation, and is stored hashed with a secret pepper mixed in. It never exceeds what its owner can do right now, because memberships are re-resolved on every request.A token from another workspace asks for a document
GET /v1/workspaces/archivizz/projects/prd/docs/spec
Authorization: Bearer cds_live_…
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
{
"type": "/problems/not-found",
"status": 404,
"title": "Not found"
}Not a 403. Existence is the thing that leaks.
Documentation your coding agents can read
Pointing Claude Code, Codex or Gemini CLI at Docxoft is one file in the consumer repository. From there they have tools rather than a stale copy.
.mcp.json
{
"mcpServers": {
"docxoft": {
"type": "http",
"url": "https://docxoft.playxoft.com/mcp",
"headers": {
"Authorization": "Bearer ${DOCXOFT_TOKEN}"
}
}
}
}search_docs returns the matching heading or the matching OpenAPI operation with an anchor, so an agent reads one endpoint rather than compressing a 200 KB spec.write_doc creates a draft nobody else is served. Making it current needs publish_doc and a token minted with publish./v1 endpoint, described by an OpenAPI 3.1 document generated from the schemas the server validates with — so it cannot drift from what the server accepts.Dependency graph for API consumers
Consumers declare what they read and at what range. The graph is reconciled from CI on merge, so it describes what is actually deployed rather than what somebody filled in during a migration.
.docsrc.json, committed in the consumer repository
{
"project": "spendchat/mobile",
"consumes": [
{ "doc": "spendchat/api/openapi", "range": "^1.0.0" },
{ "doc": "platform/standards/flutter-style",
"range": "^2.1.0", "pin": "2.1.3" }
]
}Search
Every published version is chunked as it is published — per heading for markdown, per operation for OpenAPI — and those chunks are what search ranks.
A hit is a heading or an operation, with a snippet and an anchor that opens the reader at exactly that place. No 200 KB file to scroll.
The chunk rows are written in the same atomic batch as the version they came from. There is no sync job to fall behind and no index to rebuild.
The project filter is part of the query builder's signature, so a search cannot be run without one. Results never span a boundary you cannot cross.
Self-hosted documentation on Cloudflare
One deploy target, two stores, no service of ours in the loop. Every plan limit is unbounded when you run it yourself.
One Worker serves the app, the REST API and the MCP server, globally. Deployed with OpenNext; the bundle is measured against a gate on every change.
Metadata, the version ledger and the search index in Postgres; bodies in object storage over the S3 API. No cache tier and no search cluster to operate.
Sign-in runs through your own WorkOS AuthKit project. ID tokens are verified against public keys — there is no server SDK and no service account to hold.
No payments configuration means billing is simply off, with a no-op provider. A self-hosted instance is never nagged about a plan it does not have.
Patent grant included. Fork it, run it, keep it — the hosted deployment and the source are the same code.
Secrets are plain worker bindings and the deployment talks to your Neon project and your bucket. There is no telemetry endpoint pointed at us.
Stop copying specs between repositories
Create a workspace in under a minute, or read how the pieces fit together first.