Changelog¶
All notable changes to contextd are recorded here.
The format follows Keep a Changelog, and the project uses Semantic Versioning — with one caveat while the major version is 0:
Pre-1.0 compatibility. Minor releases may change commands, flags, the HTTP API and the on-disk layout. Pin an exact version if you depend on any of them. Breaking changes are called out under Changed with the migration in the same bullet.
Releases are cut automatically from main by CI; the tag and the GitHub release are created in the same run.
Unreleased¶
Changed¶
- The project moved to the ORKCOM organisation. Everything now lives under
orkcom-tech: the repository, the templates, the Homebrew tap, the Scoop bucket and the documentation site (now https://orkcom-tech.github.io/contextverse/).
The Go module path changed to github.com/orkcom-tech/contextverse. GitHub redirects the old path, so go get on it keeps working — but the redirect lasts only until someone registers a repository with the old name, so anything importing this module should update. Pre-1.0, and cheaper now than later.
Reinstall paths changed with it:
brew untap abyssmemes/tap && brew tap orkcom-tech/tap
brew install orkcom-tech/tap/contextd
scoop bucket rm contextverse
scoop bucket add contextverse https://github.com/orkcom-tech/scoop-bucket
The winget package is now OrkcomTech.Contextd. An existing install under the old identifier keeps working and will not upgrade across the rename; reinstall from the new one.
- The licence names ORKCOM as the project's home, not as the rights holder.
Licensorand the copyright line stay with Eduard Lugovtsov, with(ORKCOM)in the position(ContextVerse)used to hold. The company is not registered yet, and an unregistered entity cannot hold copyright or grant a licence — putting one in the Parameters block would look tidier and mean less.
Added¶
-
contextd export --format single— the whole context pack as one Markdown document on stdout, so it composes:contextd export --format single | pbcopy. The existing export writes a folder of numbered files for ChatGPT's Knowledge upload; everything else — a chat box, a UI with one context field, a colleague asking what your setup is — wants a single document, and the answer used to be to concatenate five files by hand. Sources that are missing from the space are named in the output rather than skipped, because a pack quietly missing your principles is worse than one that says so. -
Issue and pull-request templates, and a security policy. There were none. Written for this product rather than from a generic checklist: the bug report asks whether your space was created by the version you are running or carried across an upgrade, because that one question separates the state most of this project's bugs lived in from the state every test used to start from. The pull-request template asks how you know a new test would have failed before your fix.
SECURITY.mdroutes reports through GitHub private advisories, and states plainly the two things that are deliberate rather than vulnerabilities: the local console is loopback-only with full access to the space it serves, and a model can ignore context it was handed.
Added¶
contextd space sync set <space> <path> <mode>— change which paths travel between a server and its clients. The rules were visible inspace showand editable only by hand-editingmeta.yamlon the server, so a capability the product already had was effectively undiscoverable.identity/ init-onlyis the right default for a team, where one person'sme.mdmust not land on everyone; it is the wrong default for one person syncing their own two machines, and that is now one command rather than a text editor and an SSH session.-
The documentation says how to get the same space onto a second machine — rebuild it, keep it in git, or run your own server — instead of implying there is one way. Which is right depends on whether the second machine is yours or someone else's, which the tool cannot know and should not decide.
-
contextd bench context— the eager-vs-lazy measurement. The product sends an AI its whole entry set at every session start, and whether that beats letting the client fetch what it needs had never been measured; that is why no token-efficiency claim appears anywhere in the documentation. Three arms — nothing, the entry set, a token-budgeted graph map plus the retrieval tools — over a task set of questions with known answering documents. Deterministic and free: it measures whether an arm can reach the answer and what reaching it costs, not whether a model then answers better.
First result, and it is not the flattering one. On the default space the map arm reached 5/5 answers for 318 tokens against the entry set's ⅗ for 699 — until the questions were rephrased to share no vocabulary with their documents, at which point the map arm reached 0/5. The retrieval policy is keyword search, so the first run was measuring how the questions were written by someone who already knew the answers. The eager-vs-lazy question is therefore still open, and both task sets are committed so the limitation stays visible rather than becoming folklore.
One finding does not depend on phrasing and is stable across both sets: the entry set reaches only 3 of 5 answers because two of the answering documents are not in it. A fixed list cannot deliver what is not on it, so today's session payload is not merely expensive, it is incomplete.
- A test fixture for the state real installations are actually in.
internal/testspacebuilds a space as a pre-v0.7 build left it: documents on disk, an empty version log, no.contextverse,space-index.mdfrozen in the old format. Every test in this repository used to start from a space the current build had just created — the one state a user is almost never in. Four shipped bugs lived exclusively in the other state, which is why a green suite meant nothing; they were found by a person on a second laptop instead. - Golden snapshots of what the TUI draws. Each tab is rendered at a fixed size against that fixture and compared as text (
go test ./internal/tui -updateto rewrite). Everything reflecting the machine rather than the space — the space's own path, which plugins happen to be installed — is fixed before the panels are laid out, not redacted afterwards, because a temp directory is short on Linux and long on macOS and the panel wraps one and not the other. A separate test asserts the invariant directly: the same space rendered from two different locations has to draw the same screen. The three tabs that contradicted each other were each individually defensible — every function returned what its own test expected. Nobody looked at the screen, where "8 files" and "(no tracked files)" sat two keystrokes apart. That now appears in a diff, in the words the user reads. - Tests that run whole commands rather than the functions inside them.
contextd init soloend to end: flags alone with no extra--non-interactive, refusing to overwrite without--force, overwriting with it, and a fresh space having tracked files immediately. Each covers a bug that shipped because the function worked and the command did not. contextd freshness checkgained--json/--yamland--fail-on-stale. The scan is the one piece of context hygiene a build can enforce, and it had no machine-readable output and no way to fail. The gate has a test that writes a document four years past its window and asserts the command exits non-zero — a gate nobody exercises is a gate that quietly stops holding.--json/--yamlonplugin list,acl listandhistory list. Detection in particular is worth scripting over: it answers "is this machine set up for Cursor" without grepping a marker out of a table.history listnow reports timestamps as RFC 3339 in UTC — a snapshot listing is the input to a restore decision, and a local time with no zone cannot be compared across machines.--json/--yamlonaudit list,audit stats,audit verifyandbackend list. The audit log is the surface most likely to be read by something other than a person — a compliance check, a nightly job — and it could only be scraped. Structuredaudit listkeeps full targets, where the table shortens them to stay readable: a truncated path is a path a script cannot open.- A test that the table and
--jsondescribe the same set. The failure mode of structured output is not a malformed field, it is the two renderers drifting until a script and a person reading the same command disagree. Run against an upgraded space on purpose: on a fresh one every file is tracked, so a renderer dropping untracked files produces identical output and the test proves nothing. -
Link validation for the local console. Every
hrefa local page renders must resolve to a route the local mux actually registers. Checking status codes was never enough — a page can answer200and still be full of links that do not. -
The sync daemon can now start at login.
contextd daemon installwrites a per-user launchd agent (macOS) orsystemd --userunit (Linux);daemon uninstallremoves it,daemon unitprints it without installing. Deliberately per-user, never system-wide: the daemon reads your token and syncs your space, so running it as root would use the wrong identity against a credential it should not be able to read. contextd daemon logs [-n]— the log has always been written to.sync/daemon.log, with no command to read it.contextd daemon statusgained--json/--yaml, the last sync time, and whether autostart is installed.- The client wizard now offers background sync. It existed with no way to discover it: nothing in setup mentioned it, and nothing installed it, so a working client went stale the moment its terminal closed.
contextd ui— a local web console for solo and client spaces, on demand: it prints a URL, serves until Ctrl-C, and exits. Bound to loopback, a fresh one-time key per run,HostandOriginvalidated so a page in another tab cannot drive it, and double-submit CSRF on writes.contextd ui installkeeps it running for people who want that, after confirming the trade. Deliberately not on by default: a standing web server with write access to your context files, for someone who may never open it, is a door left open — the TUI covers the same ground and listens on nothing. It shares the server console's templates and stylesheet rather than duplicating them.contextd search <query>— find text in your own space: paths and contents, case-insensitive substring by default,--regex,--pathglob,-lfor filenames only, and structured output. The MCP server has exposed search to AI clients since v0.1.0, so an assistant could look through your space and you could not. Both now run the sameinternal/search, because two implementations of "what is in here" is how a tool starts telling a person and their assistant different things.contextd file diff <path>— a unified diff between two versions, defaulting to the previous one against the current: the usual question of what a change actually did.--from/--tofor any pair,--statfor counts,-Ufor context width.file historysaid a version existed andfile getprinted one; nothing showed what moved.space-index.mdandteam/space-map.mdare now generated from the graph. The index came from a hardcoded format string whose Dependencies column was a literal em dash, and the map was a drawing seeded once at init that never learned about a file again — whilecontext-entry.mdtold every AI to read the index for "what exists". Both are rewritten through the FileLog, so regenerating them is an ordinary versioned edit, and identical output creates no new version.contextd context inject --mode map— hand an AI the map and the means to fetch, instead of the documents. On a ten-document space the map is 222 approximate tokens against 532 for the entry set, and the gap widens with the space because the entry set grows with content while the map grows with its budget. Opt-in on purpose: which one a model actually does better with has not been measured, and switching the default on an unmeasured belief is the mistake the benchmark exists to prevent.- The graph has surfaces. A Graph tab in the TUI (tab 6) where Enter opens a document's connections and again walks to one of them, and a Graph page in the local console listing every document with what it links to, the broken links, and the orphans. The console offers Mermaid source rather than drawing the graph: rendering one needs a layout engine, and vendoring a megabyte of JavaScript for a single page is not a trade worth making.
- MCP gained
context_mapandcontext_neighbors, so an assistant can navigate the space — ask what a document connects to — rather than be handed all of it. contextd graph— the map of your space, derived from the links your documents already contain.[[wikilinks]], Markdown links and bare paths all resolve; backlinks, orphans and broken links fall out of it; ranking is PageRank over the links, lifted by a document's declaredimportanceand cut in half when it has gone stale.graph <path>shows a document's neighbourhood,--orphansand--brokenthe two ways a map comes apart,--format mermaida picture.contextd statusgained a one-line summary.- Documentation that points at dead code is now visible. A path like
./scripts/deploy.shinside a project's document is resolved against the checkoutactivaterecorded and reported when it no longer exists. Nothing else connects written procedure to the code it describes: editors map your code and know nothing of your decisions, note tools map your notes and know nothing of your repository. contextd activatenow records where a project lives. It has always known the working directory and the project name and persisted neither, so the space could describe a project without knowing where on disk it was. That anchor is what will let the context graph check a document mentioning./scripts/deploy.shagainst real files. Per-machine by design: a checkout path is local truth, not something to push to teammates.
Changed¶
- The CLI reference had fallen behind the binary.
search,graph,ui,bench,file diff,space adopt,space sync set,daemon install/uninstall/logsandcontext inject --mode mapall shipped without reachingdocs/cli.md; the list of commands supporting--jsonnamed eight and is now twenty-one. Both directions are now checked against the built binary rather than by reading. - The quickstart stopped at setup. It walked you through installing and syncing and never showed you the commands you use afterwards —
search,graph,freshness check,tui,ui. - The graph reached the front page. It shipped as the most distinguishing thing in the product and appeared in neither the README's capability list nor the documentation site's. Stated as what it is: a map derived from links you already wrote, where a connection exists because a document says so rather than because something inferred it.
- The server guide now documents selective sync, which is where an operator looks when deciding what a space shares.
-
The templates repository explains how to check a recipe against the binary —
plugin listto confirm detection fired and which rule matched, thenactivateagainst a throwaway space to read what themerge:strategy actually wrote. A wrongmerge:looks identical to a right one until you look. -
The console is minimal brutalism now. It led with a pink-to-violet gradient sidebar, a serif display face, soft shadows and 16px radii — decoration carrying no information, on a tool for people who live in a terminal. One monospace family (every value on screen is a path, a version or a command, read character by character), flat surfaces with hairline borders, square corners, and colour reserved for meaning: accent for the current thing, red for danger, amber for stale. Long content no longer pushes the page sideways. Web fonts dropped, so a machine that is offline or behind a proxy renders what everyone else sees — and a tool that edits your files does not announce every page load to a font CDN.
- A Documentation link now sits in both sidebars.
Fixed¶
contextd file historyexited0for a path that does not exist, so a script could not tell "not under version control yet" from "you typed it wrong". A missing path is now an error with a non-zero exit; a file that is in the space but has no versions still succeeds, says so, and reports"tracked": falsein structured output.-
The community catalog and the binary disagreed about Cursor.
merge:was corrected toreplace-filein the embedded copy and left asmarked-blockincontextverse-templates. Because a fetched catalog overrides the embedded set, the samecontextdbehaved one way offline and another way after fetching. The catalog now carries theopencodeintegration too — it was embedded but absent there, which made it the one integration nobody could fix by pull request.contextverse-templatesgained CI that fails a pull request when a mirrored integration drifts from the binary. -
contextd init serverignored--server-dirand wrote to the default location. Every other server command honours the flag, so the one command that creates a server was the one you could not point somewhere else — and it did not say so, it just used a different directory. A test that creates a throwaway server would have edited the developer's real one. -
A client pushed
identity/me.mdinto the shared space.identity/is declaredinit-only— seeded from the server once, then yours — and pull honoured that, refusing to overwrite a copy it had already seeded. Push skipped onlynever, so a real person's name, role, tools and preferences travelled up into the space the whole team pulls from. The asymmetry is what kept it invisible: nothing on your own machine ever changed, and the file still left.init-onlynow means the same thing in both directions. -
audit statsprinted a different order every run, because it ranged a map. Two identical invocations produced different output, which makes the command impossible to diff and unnerving to read. Sorted by count, then by name so ties stay put. plugin listprinted raw tab characters, so the table came out ragged in most terminals. It is column-aligned now, like every other listing.freshness checkreported stale windows as2160h0m0s. Documents declare90d; the table printed the same fact in a form nobody writes down. Both the table and the structured output now use the units the document used.- Nothing in the local console could be opened by clicking it. The file table built its links from the space's display name, which on a server is also its route and in the local console is the directory's basename — so every row pointed at
/ui/spaces/.context/...while the console answers on/ui/spaces/local. Routing identity and display name are separate fields now. - The console's menu offered a Freshness page it does not serve. One dead link was removed when the console shipped and the one beside it was missed. There is now a test that resolves every rendered link, which is how both were found.
-
The console listed the version log rather than the space, so a space carried across an upgrade looked empty in the browser while its documents sat on disk. The same fix had already been made in the CLI and the TUI; the third surface was missed. All three now share one implementation (
internal/spacefiles) — four hand-written copies of "what is in this space" agreed only on spaces this build had just created, and three fixes for one bug is the usual arithmetic of duplicated logic. -
The Files tab and
file listshowed nothing while the space was full of files. They listed the version log; the Space tab counted the directory. So one tab reported "identity 1 files, team 3 files" and the next said "(no tracked files)" with nothing to select and nothing to open — the tool contradicting itself about its own contents, which is what made it feel broken. Both now list what is in the space, marking a file with no version yet as—; opening one recordsv1.space adoptremains for backfilling history deliberately, but nobody has to run it to see their own documents. - The setup wizard asked whether to overwrite an existing space, was told yes, and then refused. It confirmed the decision but never passed it to the code that creates the space, so every remaining question was answered before the run failed — the worst possible place to fail.
--forcewas dropped on the same path, making the flag a no-op exactly when it was needed. - Spaces created before version tracking existed showed as empty. Their documents were on disk with nothing in the version log, so
contextd file listsaid "(no files)" while eight of them sat right there, the TUI Files tab was blank, and there was nothing to open — the tool contradicting itself about its own contents.contextd space adoptrecords them asv1, andactivatenow does it automatically when it finds a log that knows nothing about a tree that has content. The earlier fix only covered newly created spaces; every space that already existed stayed broken. contextd init sologave four bare prompts instead of the guided setup. The wizard was added tocontextd init, andinit solo— the command everyone types out of habit, and the one every version of the documentation has printed — was left on the old path. Run interactively with no flags, it now runs the same guided setup.init clientlikewise.- Flags alone were not enough to set up in one command. Passing
--nameand--rolestill stopped to ask for them unless--non-interactivewas also given: a flag naming the mechanism rather than the intent. Supplying flags now means using them.--non-interactiveremains for scripts that want the guarantee. -
The local console linked to a page that does not exist there. The shared space template offered "All spaces", which belongs to a server.
-
A relative
--dirsent config writes to the wrong file.space_rootwas stored as whatever string--dircarried at init, andSaveresolved it against the calling working directory — so runningcontextd activateinside a project wrote a strayconfig.yamlunder that project and left the real one untouched. In client mode that silently droppedSync.LastHead, so the client re-pulled the whole space every run and pushed against a stale head, inviting conflicts that were not real. The space root is now resolved to an absolute path on load and on save.
Fixed¶
- A fresh space had no version history at all.
space.Createwrites the template straight to the working tree, so nothing it seeded was ever recorded in the version log:contextd file listreported "(no files)" on a space containing eleven Markdown files,file historywas empty for every one of them, and the Files tab in the TUI showed nothing. History began at the first write through contextd rather than when the content began. Setup now records the seeded tree asv1.
Changed¶
- The daemon backs off when the server is unreachable. A fixed ticker meant an identical failed request every interval forever — log noise, load on a server that is probably already unwell, and a flat battery on a laptop off the VPN. Failures now double the interval up to 15 minutes and the first success restores it. A single failure still retries at the normal interval, so one dropped packet does not push the next attempt out to minutes.
0.7.0 — 2026-07-27¶
The release that made the command surface match what the product claims. Most of the work is presentation rather than new capability, because the capability had outgrown the way it was exposed: 28 flat top-level commands, no guided path after install, no machine-readable output, and documentation describing several commands that were never built.
Added¶
- Guided setup.
contextd initwith no subcommand is now a wizard: it picks the mode with you, explains every option as you choose it, offers a template from the catalog, sets up a storage backend, wires the AI tools it detects, and finishes by naming every file it created and what each is for. contextd init --reconfigure— change identity, AI tools, storage backend, default editor or (in client mode) the server connection, on an existing installation.- File editing from the CLI and TUI.
contextd file edit <path>checks the file out into$EDITORand writes it back as a new version;contextd file put <path> --from FILE|-does the same from a file or stdin. In the TUI, Enter on a file opens an editor picker. - Server-side space management in the CLI —
contextd space create|list|show|delete. These existed only in the web console and the HTTP API, which inverted the project's rule that a capability lives in the CLI first. - Structured output. Global
--json/--yamlonstatus,file list,file history,user list,space list,space show,pullandpush. - Meaningful exit codes —
0success,1usage or local state,2network/auth/permission,3compare-and-swap rejected. A pipeline can finally tell "retry later" from "you called this wrong". - opencode support — detected via
opencodeonPATHor its config directories; context is delivered into a marked block inAGENTS.md. - Installers hand off to setup.
install.shandinstall.ps1offer to runcontextd initwhen a terminal is attached, and print the command when one is not.
Changed¶
- Root help is grouped by task instead of listed alphabetically: set up and inspect · work on your context space · sync and storage · deliver context to AI tools · administer a server · interfaces and help.
- TUI keys. Enter on a file now edits it; version history moved to V. v previews the live file body — previously it did nothing at all on the file list.
- Help text in both the TUI and the web console is grouped with the same headings as the CLI.
- Empty states teach: the TUI on a directory with no space, and the server TUI on an uninitialized data directory, now name the path they checked and the command to run.
- Internal phase numbers removed from user-facing command descriptions.
Fixed¶
- Client integrations no longer overwrite files they do not own.
applySlotignored themerge:field declared in every integration and always rewrote the whole target. ForAGENTS.md— hand-written, and read by several agents — the firstcontextd activatewould have destroyed it.marked-blockmerging is now implemented: only the delimited region belongs to contextd, re-runs replace rather than stack, and a half-written block is refused instead of guessed at. - GUI editors are always launched with their wait flag, including when
$EDITORomits it. Without it the editor forks, the caller reads the file back unedited, and the user's work is silently discarded. - Two tests that had been failing CI: the integration test posted to the console login without the CSRF handshake a browser performs, and an audit test asserted Unix permission bits on Windows, which does not implement them.
Security¶
- Editing is refused wherever the shell escape is refused. A text editor is a shell escape (
:!sh,^R^X,M-!), so allowing it in the SSH-served admin TUI would hand a shell to the locked-down service account and bypass RBAC — the exact hole the existing shell-escape gate closes. contextd space deleterequires--yesand reports the file and byte count it is about to destroy, because it removes the version history with it.- Carries forward the hardening from the same cycle: per-resource authorization in the console and event stream, every filesystem sink behind one path validator, bounded token lifetime, and no trust in forwarded identity headers.
0.6.0 — 2026-07-23¶
Changed¶
- Space UI restyled to match the cloud SPA shell.
0.5.0 — 2026-07-23¶
Added¶
- HTTP API for users and policies.
- A bootstrap admin token is written on server init.
Fixed¶
- CI module downloads retry through proxy flakes.
0.4.0 — 2026-07-23¶
Added¶
- DNS-01 ACME, OTLP tracing, Windows service support, and stateless HA.
- Web UI brought to parity with the CLI for the operations it exposes.
Fixed¶
- Windows service test no longer attempts a real install on CI.
0.3.0 — 2026-07-23¶
Changed¶
- CI and minor releases are gated on product code paths, so documentation and workflow edits no longer cut a release. A manual run skips the gate deliberately.
0.2.0 — 2026-07-23¶
Added¶
- Docker and Helm templates for running the server.
0.1.0 — 2026-07-23¶
The first substantial release: server, storage, governance and the AI-delivery surfaces.
Added¶
- Server with pluggable storage (local filesystem, git, S3, SQL), an admin web UI, and path-based ACL.
- Per-file versioning (
FileLog), the terminal UI, and session-start plugins. - Governance — audit log, outbound webhooks, per-user ACL with deny-wins path exceptions, rate limits, storage quotas, and freshness nagging.
- Secret-scan guardrail on push and on file writes.
- Observability — Prometheus metrics and an SSE live event stream.
- ACME TLS and an OSS auth-method registry.
- MCP server on stdio (
context_status/list/get/search). - Template catalog fetching and caching, community client-integrations, shell completions, a ChatGPT export bundle, Linux packages, MkDocs user documentation, and branch-aware CI with automatic minor releases.
0.0.1 — 2026-07-22¶
Added¶
- Initial scaffold: the
contextdbinary and its installers in one repository, under BUSL-1.1. - Core solo workflow —
init solo, the space model,activate,status. - Install scripts and the GoReleaser pipeline.