Repository navigation
Releases
doction 0.33.0
The application now presents a page the way https://doction.site does, and behaves like an app
on the iPhone. No API or MCP contract changed.
Changed
- A page looks the same in the app and on the site. The page title is 36px, the size the
site uses (52px is kept for a landing hero). The header is compact: breadcrumbs, title, then
one row with the metadata and the page's actions, so Delete is no longer a row of its own.
The document's first block starts right under the header instead of 64px further down. - The canvas is flat. The diagonal sheen behind the document is gone, along with its
--material*tokens. - The top bar does not repeat the title. On desktop it shows the page title only once the
page's own title has scrolled under it. Mobile is unchanged. - "You are here" in the page tree is the green rule, weight and ink, with no filled surface,
the same mark as the site's navigation.--nav-active-bgis removed. - The mobile drawer slides in instead of only fading.
DESIGN.md§3, §7, §8 and §9 describe the result.
Fixed
- On touch screens the sidebar page tree scrolled sideways: each row's 44px touch area overhung
the panel edge. - On a phone, a wide table widened the whole page. It now scrolls within itself.
- Dragging the scrim behind the open mobile drawer scrolled the page underneath.
- Safe-area insets were ignored on iOS because the viewport lacked
viewport-fit=cover; the
top bar, content and drawer now also respect the side insets in landscape. - The Inbox and Graph links in the sidebar were underlined.
- Code blocks showed a light native scrollbar on their dark slab.
- The editor's title was set in a synthesized bold: Instrument Serif ships only in 400.
doction 0.32.0
A finished-product pass over the application and https://doction.site, and one visual language
across both: green for the brand and "you are here", orange only for notes. No API or MCP
contract changed.
Added
- The documentation site presents itself when shared or indexed: a
robots.txtnaming the
sitemap, a title and description of its own on every page (derived from the page when it
declares none), a descriptive home title, and Open Graph / Twitter card tags with a social
image built from the design tokens. - A 404 in doction's voice on the site, linking Installation, REST API, MCP and Home,
instead of the framework's generic page. - The site build checks what it built: it fails, naming the page, when a page lacks its
title, description or social tags, whenrobots.txtis missing, or when anything would load
from another host — the offline guarantee was previously verified by hand.
Changed
- One brand mark, in green. The terminal glyph is
--nav-greenin the app's sidebar (it
was orange), and the app's favicon and installable-app icons, still in the retired blue
palette, now match the documentation site.icon-512.pngis full-bleed, as itsmaskable
entry requires.theme-colorand the manifest use the current canvas and chrome tokens.
DESIGN.mdrecords the brand mark. - The app and doction.site assign colour the same way. Green marks the brand and "you
are here": the active page, table-of-contents entry and settings section carry a 2px green
rule in the app (it was orange; the green measures 6.92:1 against the active surface), and
the site's active navigation entry carries the same rule. Orange is left for one job, the
rule beside quotations and admonitions. Breadcrumb separators are subtle ink. - The wordmark is "doction" in the sidebar and the webhook help text, as everywhere else.
- The favicon, manifest and touch icon are linked with a content hash, like the stylesheet,
so a changed icon reaches the browser instead of waiting out its favicon cache. - A browser address outside the application that matches nothing —
/settings,
/w/x/graph— redirects to the same path under/appinstead of answering with raw JSON.
API clients and requests that do not ask for HTML keep the JSON 404.
Fixed
- Links in running text on the documentation site are underlined, as they are in the app:
they were distinguished by colour alone (WCAG 1.4.1, reported by axe-core in both themes).
doction 0.31.11
Documentation, CI and repository hygiene, plus the fixes for issues #42, #44 and #45. No API
or MCP contract changed.
Added
- Public documentation site at https://doction.site.
docs/index.mdis a landing page
(what doction is, how it works, the REST and MCP entry points, self-hosting, a map of every
page), served at the root by a small MkDocs hook with the README at/introduction/. The
site uses the application's own visual language — its tokens, the vendored Manrope,
Instrument Serif and JetBrains Mono, the terminal mark — and loads nothing from a
third-party host: no Google Fonts, no GitHub API call, no remote badge images.
pages.yamlredeploys it on every push tomainthat touches the documentation.
Changed
- CI on
maintakes about 4 minutes instead of 10. Thetest,webandpublishjobs
shared one build-cache scope and overwrote each other, so only 3 of 59 steps were ever
cached; each now has its own, andpublishreuses thewebjob's layers (6:48 → 0:49).
The SPA stage is built once on the build host instead of again under QEMU for arm64. The
runtime'sapt-get upgradelayer is refreshed weekly (APT_REFRESH, the ISO week) so a
working cache does not freeze Debian's security fixes. openspec/andAGENTS.mdare no longer versioned: both are maintainer-local, gitignored
likeCLAUDE.md, and no tracked document points at them.make specskips when
openspec/is absent.- Pending work is tracked in GitHub issues (#60–#65) rather than a roadmap file.
- The README is a short tour — screenshots, a quick start, the licence. Documentation that
said the same thing in two places now says it once and links: features and the page map
ondocs/index.md, licence obligations and the production checklist in
docs/configuration.mdanddocs/operations.md, the code-scanning policy in
SECURITY.md, the relicensing history in the README. DESIGN.mdis in English, and comments, docstrings and log messages acrossapp/,
tests/,scripts/andevals/were trimmed and translated to English. No behaviour
change.- The sign-off check skips Dependabot pull requests.
Fixed
- Search snippets no longer show markdown syntax — table pipes, heading marks, wikilink
brackets or mermaid source. The syntax is stripped before the words are chosen, so ranking
is unchanged (#44). - The graph view's force layout is readable: labels are centred under their node and sized
into the simulation, so they no longer overlap (#45). - The seeded deploy runbook described a retired Gitea runner and SQLite; it now describes the
deployment that exists (#42). scripts/changelog.pyrefusesUnreleasedas a version instead of printing that section
as release notes.
Removed
ROADMAP.mdand.graphifyignore.
Dependencies
reactandreact-dom18.3.1 → 19.3.0 (major).lucide-react1.43.0 → 1.47.0,prettier3.9.6 → 3.9.8.pyjwt2.13.0 → 2.14.0,onnxruntime1.29.0 → 1.30.0,ruff0.16.6 → 0.16.8,
pyright1.1.411 → 1.1.414.- GitHub Actions:
docker/setup-buildx-action4.4.1,docker/build-push-action7.4.0,
docker/setup-qemu-action4.4.0,astral-sh/setup-uv10.1.0.
doction 0.31.10
Repository hygiene. No application behaviour changed — this release touches demo
content, documentation wording, an OpenSpec rationale, a local tooling config, and a
handful of dead files.
Changed
- doction no longer frames itself around a specific device. The seeded demo runbook,
docs/install.md,docs/operations.md, and the rationale prose in the
client-resilience,visual-language, andretrieval-rankingspecs now describe the
actual constraint (self-hosted, modest hardware, often behind a VPN with no public
route) instead of naming "Raspberry Pi" as the product's identity. The Raspberry Pi
stays as a real, measured example where docs already cited concrete numbers — this is
wording, not a retraction of those measurements. .graphifyignoreno longer duplicates.gitignore. It kept 3 entries whose exclusion
Graphify does not already get for free —app/static/vendor/(vendored, minified,
git-tracked on purpose),graphify-out/, and a broader*.log— down from 25. This
also fixes a real bug: a duplicated.env.*rule was re-excluding.env.examplefrom
the knowledge graph even though.gitignore's own!.env.exampleexception says it
should be readable.
Removed
.agents/skills/.openspec-target, a tracked leftover from an agent-tooling scaffold
retired incb5e85d(it namedcodex, not this project's tooling, and nothing
referenced it).- The empty
tools/directory, unreferenced and without git history.
doction 0.31.8
No application code changed. This release exists because the image content changed and
publishing different content under an existing version tag contradicts what
release-integrity is for — see the roadmap entry about publish running on every push.
Added
AGENTS.md, shipped, explaining the one thing nothing else did: behaviour here is
specified before it is written,openspec/specs/holds the contracts and
openspec/changes/archive/holds the reasoning. The repository was already shipping 112
OpenSpec files whileCONTRIBUTING.mdmentioned openspec once, as a command inside the
gate, and the README anddocs/not at all. It is a map rather than a copy: everything
already documented elsewhere is a pointer, because one truth in two files becomes two
facts that disagree.openspec/README.md, explaining the two directories and how to read a capability
versus an archived change — including that the notes at the end of an archivedtasks.md
are where a decision's reasoning lives.ROADMAP.md, making nine identified-and-deferred items visible, in priority order,
with no dates. Each names where it was identified. Five became labelled issues; four are
decisions for the maintainer and say so, because an issue tracker misrepresents a decision
as available work.- A discoverability check (
scripts/check_docs_reachable.py), in the gate and in CI: no
tracked document may reference an untracked file.CLAUDE.mdand.claude/are gitignored
deliberately, which makes a document citing them a dead end for everyone but the
maintainer. - A sign-off check (
scripts/check_signoff.py) on pull requests. Every commit must carry
aSigned-off-bymatching its author, certifying the terms the work is offered under. It
was verified against constructed commits rather than trusted, since every commit in this
project's history is a direct push tomainand the check will not fire for a long time.
Changed
CONTRIBUTING.mdnow says proposals precede code, points at the workflow it never named,
requires sign-off with the exact commands, and states the consequence: sign-off transfers
no copyright, so doction cannot be relicensed without every contributor's permission. It
also records that an item a change defers reaches the roadmap rather than staying in that
change's notes.
doction 0.31.7
Security
-
Fixed a quadratic denial of service in wikilink parsing. The pattern scanned
[[-repeated content with an unterminated tail in time proportional to the square of its
length: 24 KB cost 7.9 seconds of CPU, while 80 KB of legitimate wikilinks cost 3.6 ms.
extract_linksruns inside the page save, synchronously, in the request path, and there was
no limit on page size, so any workspace member could make one request consume minutes of
CPU. On a single-process deployment that blocks everything.Excluding
[from the target class is what removes the quadratic behaviour, since a scan
started at the wrong position can no longer run past the next[. Measured at 1869 ms →
0.10 ms on the adversarial shape, with identical results on real wikilinks. A regression
test asserts the ratio between adversarial and benign input of the same length, and was
verified to fail against the old pattern.Found by CodeQL (
py/polynomial-redos), where it had sat unread for weeks among twenty
findings that were not real. -
Page content is now limited to 1 MiB. Not the fix for the above — that is in the
pattern — but a ceiling on what any single request can cost when a parser later turns out
to be worse than believed. The value is ~55x the longest markdown document in this
repository. Enforced where REST and MCP converge, because MCP calls the database layer
directly and would have bypassed a check on the request models. -
The runtime image no longer carries pip or uv. The application starts from a
virtualenv that is already built, so neither is needed to run it. That removes
setuptools(CVE-2025-47273, CVE-2026-59890) andmsgpack(GHSA-6v7p-g79w-8964) with
them: both were vendored inside pip rather than installed as packages. -
Debian security updates are applied in the runtime image, closing 13 findings against
libpcre2-8-0,libsqlite3-0,gzip,libc6andlibc-bin. Each had a fixed version
published within the same Debian release, so none was dismissible. This adds about 75 MB
to the image: Docker layers are additive, so upgrading a package writes the new files
above while the originals remain below. -
The code scanning queue is triaged, and staying triaged is now the rule. Eleven
findings were dismissed with a specific written reason rather than the words "false
positive"; eight were fixed in code.CONTRIBUTING.mdandSECURITY.mdrecord that an
alert is fixed or dismissed-with-reason and never left undecided, and that an additional
scanner — OpenSSF Scorecard is the pending case — is enabled only once the queue is at
zero.
Fixed
scripts/changelog.pyreported an error naming--declared, a flag renamed to--check
when it was introduced, and had a code path a static analyser read as leaving a variable
unassigned. Both corrected.- Four tests performed a
DELETEinside anassert, so running underpython -Owould have
stripped the request and passed without testing anything. - Two wrapped SQL statements in the schema list are now explicitly parenthesised. Adjacent
string literals in a list are indistinguishable from a forgotten comma.
doction 0.31.6
Added
- A GitHub Release is published when a version tag is pushed, with that version's
CHANGELOG.mdsection as its body. 40 tags existed and no releases did, so nothing on the
repository said what a version contained. The workflow triggers on
v[0-9]+.[0-9]+.[0-9]+rather thanv*: this repository carried a mistyped tag named
rm, and the looser pattern would have published a release for it. Releases start here and
are not backfilled — the changelog describes earlier history in ranges, so per-version
notes for those 30 tags do not exist and would have to be invented. - Published images carry an SBOM (1312 packages, SPDX-2.3) and SLSA provenance at
mode=max, which records the build steps and resolved inputs rather than only the builder.
Provenance was already being emitted atmode=minby default; what changed is the detail
level.docs/operations.mddocuments how to read both from an image you pulled. - A version with no
CHANGELOG.mdentry now fails the gate. Release notes are derived
from that file, so a version nobody described is a release nobody can read.
scripts/changelog.pyis one parser with two callers: the gate asserts the version
declared inpyproject.tomlhas a section, and the release workflow prints that section as
its notes. It checks the declared version rather than the tag, because at tag time the tag
already exists and the ruleset below forbids deleting it.
Changed
- The four model downloads retry transient failures (
--retry 5 --retry-delay 5). The 0.31.4
publish died on HTTP 429 from Hugging Face; the content is pinned by revision and verified
by checksum, so that was availability, not integrity, and a release stopped by a momentary
rate limit is a release stopped for no reason.--retry-all-errorsis deliberately omitted
so a 404 or a renamed revision still fails promptly.
Security
- A published version tag can no longer be moved or deleted. A ruleset blocks deletion,
non-fast-forward, and any update to av*tag. The middle rule alone was not enough and
the test proved it: moving a tag to a later commit is a fast-forward, which
non_fast_forwardpermits. For a version, every move is wrong. - The stray
rmtag is gone. It had only ever existed locally.
doction 0.31.5
Changed
-
Relicensed to AGPL-3.0-only.
LICENSEcarries the verbatim GNU Affero General Public
License version 3 (661 lines, the FSF text, sha2560d96a4ff…079abcb0).The reason is narrow. doction is hosted for people, and the plain GPL attaches its
obligation to distributing a copy. An operator never hands anyone a copy, so a modified
fork could be sold as a hosted service with its changes kept private. The AGPL's section
13 attaches the obligation to network use instead, which is the case doction actually has.Relicensing is not retroactive. Releases through 0.31.3 were published under MIT and
0.31.4 under GPL-3.0-only. Both grants stand and are not withdrawn: anyone who received
those versions keeps those terms. AGPL-3.0-only applies from this release onwards.Dependency compatibility was re-established for AGPL-3.0 before the change: MIT, BSD-2/3,
ISC, Apache-2.0, MPL-2.0, PSF-2.0, 0BSD, Zlib, CC0, CC-BY-4.0, Blue Oak 1.0.0, and
LGPL-3.0-only forpsycopg. Nothing in the tree is GPL-2.0-only or proprietary.
Added
SOURCE_URLand a licence field onGET /api/system, surfaced in Settings, so a running
instance offers its source to the people using it. It defaults to this repository, so an
unmodified deployment complies with no configuration, and it is configurable because an
operator who modifies doction owes their users their source, not this project's.- A licence-consistency check in
make check. The licence is declared in seven places and
one of them, the published repository description, said "MIT licensed" two releases after
MIT was replaced without anyone noticing. The check readspyproject.tomlas the single
authored source and fails when any other declaration disagrees.