Repository navigation
Releases
AKMON v0.6.0
AKMON 0.6.0 follows 0.5.0. Its main change: the installable Python package is
now named akmon, and this is its first release on PyPI, so pip install akmon
and uv tool install akmon work. The command and the Python import keep their
names, so every command below still starts with okg and code still says
import okg. It also removes 8 summary views that nobody read from the console.
There are no new features beyond the rename and no new migration files.
Get it
- PyPI (new): the package is published to PyPI as the project
akmon, so
pip install akmon==0.6.0anduv tool install akmon==0.6.0work. It is
published after the release, by a separate workflow started by hand that
uploads only the wheel the release pipeline built, using PyPI trusted
publishing (PyPI trusts that one workflow, so no upload password or token
exists anywhere). Until that workflow has run, install from the attached
wheel. The 0.5.0 notes said that release would reach PyPI asokg; it did
not, and nookgrelease will. - Python package: the wheel
akmon-0.6.0-py3-none-any.whlis attached to
this release. Install it withuv tool installorpip install, then follow
the quickstart (docs/start/quickstart.md). - Containers and Kubernetes: the Install section at the end lists the
runtime and PostgreSQL images and the Helm chart, each with its digest.
What's new
- The package is named
akmon. Wheels are now named
akmon-<version>-py3-none-any.whland their metadata saysName: akmon.
okg --versionandokg.__version__read their version from that package.
Messages that suggest an extra or an added package now nameakmon. - Release tooling reads each release's own package name. Older releases
namedokgstill check, and installed upgrades pick the right wheel name for
the version they fetch. The PyPI workflow uploads only a wheel whose metadata
namesakmon, so v0.4.0 and v0.5.0 do not go to PyPI. - The console drops 8 unread views. The workspace deployment's 8 summary
views and the two Today-page builders that read them are deleted, so Home and
Sources list 65 producers instead of 73./console/viewstays. Old view
records stay in the graph.
Breaking changes
- The distribution is renamed from
okgtoakmon. A dependency onokg
must become a dependency onakmon. Pinakmon>=0.6.0. The commandokgand
the importokgdo not change, so scripts and code that use them need no edit. - Remove
okgbefore installingakmon. Both packages install the same
okgcommand and the sameokgPython package, and removingokglater
would delete filesakmonneeds. - v0.4.0 and v0.5.0 stay on GitHub, under the name
okg. Their wheels are
attached to their releases and are not on PyPI. - The route
/console/view-diagnosticsis removed. A request for console
metrics that names only retiredview:keys now gets an empty answer that
says so.
Upgrading from 0.5.0
- Remove the old package:
uv tool uninstall okgorpip uninstall okg. - Install the new one:
uv tool install akmonorpip install akmon. - Run
okg --version. It printsokg 0.6.0. - There is no new migration file and no setting changes. Run
okg up <deployment> --planandokg up <deployment>as usual.
To go back to 0.5.0, uninstall akmon first, then install the okg wheel from
the v0.5.0 release.
Known issues
- PyPI is empty until the upload workflow runs.
pip install akmon==0.6.0
works only after the separate publishing workflow has been started for this
release. - Everything listed as a known issue in the 0.5.0 notes is unchanged,
except its PyPI entry: 0.5.0 stays on GitHub.
Install
Everything below was built from the commit this tag names. The tags v0.6.0 (images) and 0.6.0 (chart) are never moved or overwritten; pin a digest to be certain.
| Artifact | Reference | Digest |
|---|---|---|
| Runtime image | ghcr.io/mitdbg/akmon:v0.6.0 |
sha256:c4e47d5b1d34228fff0e0a69cad6de88cba151b71f056ab5e1a6466df74955d8 |
| PostgreSQL image | ghcr.io/mitdbg/akmon-postgres:v0.6.0 |
sha256:20e6a145332ccf4df6f207970ac34abec217495e796575618248db099be11d1d |
| Helm chart | oci://ghcr.io/mitdbg/charts/akmon version 0.6.0 |
sha256:d2540130128009e2c47a9e0dc007c3f22a8389045d2858c1ece5a219b4a00bbd |
helm install okg oci://ghcr.io/mitdbg/charts/akmon --version 0.6.0 -f values.yaml
The chart's appVersion is v0.6.0: with images.okg.tag and digest empty it runs ghcr.io/mitdbg/akmon:v0.6.0, and its PostgreSQL image is ghcr.io/mitdbg/akmon-postgres:v0.6.0.
AKMON v0.5.0
AKMON 0.5.0 follows 0.4.0. It stops the run logs from filling the database:
a source's large record list is stored once per sync instead of five times, and
old copies are deleted after each accepted sync. It makes the graph reader
easier for AI agents to use: run counts links, adds computed columns and
accepts more call shapes; a refusal shows the one call that fixes it; an empty
page says why it is empty; and stored numbers can be totalled. It speeds up two
slow paths, link rules that match by name and joins in the query interpreter.
It also adds the first tools for replaying benchmark questions against a graph
and testing wording changes on an experiment branch, and publishes the wheel to
PyPI. There are no new migration files; the upgrade adds two columns to the
call log. AKMON's Python package and command
are named okg, so every command below starts with okg.
Get it
- Python package: the wheel
okg-0.5.0-py3-none-any.whlis attached to
this release. Install it withuv tool installorpip install, then
follow the quickstart (docs/start/quickstart.md). - PyPI (new): the same wheel is also published to PyPI as the project
okg, sopip install okg==0.5.0anduv tool install okg==0.5.0work.
It is published after the release, by a separate workflow started by hand
that uploads only the wheel the release pipeline built, using PyPI trusted
publishing (PyPI trusts that one workflow, so no upload password or token
exists anywhere). Until that workflow has run, install from the attached
wheel. - Containers and Kubernetes: the Install section at the end lists the
runtime and PostgreSQL images and the Helm chart, each with its digest. The
PostgreSQL image is unchanged from 0.4.0.
What's new
Storage
- A source's record list is stored once per sync. A source keeps a "record
set": the list of record keys it owns and the graph rows each key produced,
used to retract rows a record no longer has. Before, a record set up to 1 MB
was copied into the source's saved position, its approval record, every
approval event and the sync report (saved twice). Now a set over 64 KB
(65,536 bytes) is stored once in its own table and the other places hold a
short reference. On a lab copy of a large deployment, run-log growth per sync
fell 85% with an identical graph.OKG_SOURCE_RUNNER_INLINE_RECORD_SET_MAX_BYTES
changes the 64 KB limit. If a stored set goes missing,okg doctorwarns
source_record_set_blob_missing, naming the source. - Old record lists are deleted after each accepted sync. After a sync is
accepted, the source's older record-set and cursor copies that no saved
position or held run names are deleted once they are older than a grace
window. Before, only the manualokg db prune-source-blobsremoved them (about
0.5 GB a day on one large deployment). Set the window with
source_blob_prune_grace_secondsindeployment.yaml(default 21600, that
is six hours;0turns the cleanup off). The sync report shows the rows and
bytes removed underrecord_set_blob_prune. A failed cleanup does not fail
the sync;okg doctorwarnssource_record_set_blob_prune_failedfor that
source until a later cleanup succeeds. Do not set a very short window while
inline or command-line runs of the same source can overlap: a sync that
outlives the window can lose its copies.
Reading the graph: what agents get
AKMON's MCP server is how an AI agent reads a graph. The server still lists
eight tools: inspect, search, expand, filter, map, aggregate,
query and run.
Finding things and saying why not
- Empty search and filter pages say why they are empty. Before, the reply
said only "0 rows". Now it names the cause: a record type the graph does not
have (with the real types listed), the condition that removed every match, a
query word that matches nothing, or the query itself. It also lists up to
three ids found with that part dropped, marked "loosened" and never counted. A
check that runs out of time says so and claims nothing. Under access rules, a
record the reader cannot see changes nothing in the reply. Pages with rows
are unchanged and cost no extra reads. - A grep list row shows every matched line, up to three. Searching for exact
text lists one row per file or record. Before, a row showed only the first
matching line, so the line that held the answer could be hidden. Now a row
shows all matching lines up to three and says how many more it left out. A
reply over its size limit drops these extra lines before it drops any row. A
typical grep page is about a third larger. - A search profile's excluded paths now apply to text chunks. A profile
that excludestests/**already left out documents undertests/, but the
chunks of those documents (which carry no path) were still found and shown.
A chunk is now left out when every document it belongs to is excluded, inside
the database query, so excluded chunks cannot crowd kept ones off a page and
counts match what is shown. Under access rules (audit or enforce mode) the
check runs after the query, so a page can come up short; it then says it is
cut. These searches cost somewhat more.
Refusals that show the fix
- A refusal names the one changed call that fixes it. When a read tool
turns down a call and one small change would make it work, the reply shows
that corrected call, ready to send, inretry:, inerror.retry_calland in
fix_hint. About twenty refusal kinds now do this. The server picks the call
without reading any data, offers none when the call has a second problem, and
marks a call that is one of several possible fixes as "a guess". A shown call
passes every check that reads no data; it is not proven to return rows.
Exact numbers and links
- Stored
sum,minandmax, and one total.aggregateover stored
records now servessum,minandmaxover up to 50,000 records, and
by=[]gives one total over everything. The reply names what each total
covers and which values were skipped, with record ids. A misspelled record
type is refused with the closest names instead of being called "a stored
set". This is not served under access rules yet. - One strict rule for what counts as a number. Live and stored totals, live
filters, and computed columns that read a field as a number now read text as
a number only when it is exactly one JSON number. Values such as" 10 ",
"+5","010",".5"and"1_000"are skipped and named in the reply (a
filter names them innot_comparable). To read them anyway, use
map(compute={"n": "number(attrs.x)"}). Numbers group by numeric value, so 1
and 1.0 are one group.
Several reads in one call (run)
runcounts links from an earlier step's records. A step can name the
records an earlier step selected as the start of a link count, for example
"how many of these commits changed each file":
{"aggregate": {"input": {"kind": "edges"}, "where": {"edge_type": "affects", "src": "$1"}, "by": ["dst"], "metrics": ["count"]}}. Before, a join reached
each file once, so a count after it said 1 per file, and naming the commits
as the links' start was refused. On a 3-million-link test graph the
most-changed-files question runs in 0.3 s.runcomputes columns on a step's rows. A step can add computed columns,
such as a ratio, to the rows an earlier step holds, using the same formula
language, limits and row states asmap(compute)outsiderun, with no
outside call. Later steps read them ascalc.<name>to filter, rank, total or
call by. Before, this was refused ("does not take compute"). The same change
fixes filters on an aggregate step's text group keys (for example
site = "X"), which matched 0 rows with no note. Computed columns over
stored graph records still wait for a later release.- A join step reads its start set and links once. A
runstep that follows
links from an earlier step's records finishes in about a second on the test
graph; for 91 commits it took 5.9 s and was refused for time before, and the
whole call now takes 1.4 s, with the same records and counts. runaccepts the call shapes models send. It now takes a small
max_bytes(raised to the 4096 minimum, with a note),retainon a live step
(ignored, with a note), a kept answer (l1.…) as a step input oreach
source,input: "$n"on graph records, and constants besideeach's varying
value. The reply says what it adjusted. A kept answer's upstream calls count
toward the run's 1,500-call limit. Of 28 recorded evaluation calls that 0.4.0
refused, 16 now run fully.
Benchmarks and tracing
- Each logged MCP call can carry a benchmark question and try. A test
harness may send the question id and try number with each request (headers
X-OKG-QuestionandX-OKG-Try, orokg/questionandokg/tryin the
request's_meta). The server stores them on the call row (question_id,
try_number, shown inokg.v_mcp_calls); concurrent calls keep their own
labels. A question is one printable-ASCII token of 1 to 200 characters with no
#or,; a try is 1 to 999999. A badly formed label refuses the call with a
named error. Calls logged before the upgrade keep an empty label. - A new experiment branch keeps the attribute values its base had. An
experiment branch is a separate copy of the graph used to try a change.
Before, attribute values set by observations (stored corrections layered over
the graph) were silently dropped from the branch on its first publish, so the
branch served the original value while the default branch served the observed
one. The branch now serves the observed value. Observations made later on the
branch show only on the branch. A refused branch read now names the branch. - **Wording the server shows an agent can be...
AKMON v0.4.0
AKMON 0.4.0 follows 0.3.0. It keeps one copy of the graph's attribute data
instead of four (one test database went from 29.6 GB to 16.3 GB). It reworks
how AI agents read a graph: exact record ids rank first, a missing name is
reported as missing, the instructions name only the records a deployment has,
groups can be ranked, and every read of an outside system can be reused,
totalled and computed over without asking it again. It keeps a
source's other records publishing when one link cannot be resolved, rebuilds a
view only when its inputs changed, and keeps the scheduled publisher from
stalling behind a long sync. Upgrading applies three database migrations, one
of which deletes the old attribute copies: back up first, and read "Upgrading
from 0.3.0". AKMON's Python package and command are named okg, so every
command below starts with okg.
Get it
- Python package: the wheel
okg-0.4.0-py3-none-any.whlis attached to
this release. Install it withuv tool installorpip install, then
follow the quickstart (docs/start/quickstart.md). - Containers and Kubernetes: the Install section at the end lists the
runtime and PostgreSQL images and the Helm chart, each with its digest. The
PostgreSQL image is unchanged from 0.3.0.
What's new
Storage
- One copy of the attribute data. Before, every publish wrote the
attribute values of each record into three stored copies besides the graph
itself, checked them against each other, and refused some publishes until a
repair ran. Reads now come only from the graph's versioned records plus a
small per-version overlay, and publish no longer writes the copies. A
migration deletes them (see Upgrading). On a 29.6 GB test copy the
database went to 16.3 GB, and the next publish was correct. - Generations made by a fast-forward, a history compaction or a rollover
keep their overlays and are read normally. Before, the next publish could
lose them.
Reading the graph: what agents get
AKMON's MCP server is how an AI agent reads a graph. This release changes what
an agent finds, how much it has to read to find it, and how much it can trust
a number it is given. The server still lists eight tools: inspect, search,
expand, filter, map, aggregate, query and run.
Finding the right record
- An exact record id ranks first. Searching for one id, such as a report
number likeABC-12-345, lists the record that carries it first and marks it
as an exact match. A record whose id differs only in case or separators comes
next, marked as a variant. A record type can name the fields that hold its
ids. Before, such a record could rank fourth. - A search for one code name (with an underscore, a dot or CamelCase) finds
the record bearing that name through the attribute index instead of reading
every text that repeats it. - Search says plainly when no record has a name. When an agent searches
for one exact name or id that no record has, the reply names what was
checked (names, ids and text, in any spelling) and lists ids with similar
names; a lookup of a missing record id says "no record has id X". Before,
the reply said only "rows 0 of count unknown", and agents guessed. The line
never appears under access rules or with live data sources, nor for a
one-word name. On graphs above about 150,000 records it needs the index that
okg migratebuilds (see Upgrading).
Reading less to get the answer
- The instructions name only the record kinds a deployment has.
doc:
text records are mentioned only on deployments that declare a document
source, andfile:andcommit:ids andsource_filelookups only on
deployments that declare a git source. Before, every deployment was told to
use them, and the reads failed. A source module of your own that wraps
DocCorpusSourcemust setmcp.doc_text_nodes: true; one that wraps a git
source must setmcp.file_nodes: trueand, for history,mcp.commit_nodes: true. - The dataset overview keeps the graph's four largest sources. The short
overview an agent reads first used to hide the largest sources behind a
"+N more sources" line once the server offeredrun, becauserun's
example took their room. It now names the same four sources with or withoutrun. When
therunexample does not fit beside them, the example is left out and one
line says where the step format is described. inspecton a code function points the agent at just the function's
lines in the file's document, not the whole file.- Agents can read how a record changed.
inspecttakes two new targets:
node_history(each version of a record in a time window) and
subtype_diff(the records of one type that were added, removed or changed
between two generations). Each takes one call instead of one read per
generation and passes the same per-reader access filter as other reads. - Python code chunks start at their first non-blank line, so a cited
start_lineis the line the definition is on (a decorated function starts at
its decorator). Existing graphs keep the old chunks until each file next
changes; the first edit to a file re-embeds most of its chunks.
Exact numbers instead of arithmetic by hand
aggregateranks groups.order_byon a metric (for example
"max(attrs.size) desc") ranks every group beforelimitandoffsetcut
the page, on stored records, on live results and insiderun. Each group
gets its rank, and the reply says whether all groups were ranked or only
those from a partial read. Replies withoutorder_byare unchanged.- Every live read returns a handle. A read of an outside system through
filter,map,aggregateorexpandnow returns a handle: a short id for
the rows the server keeps for the caller. The next call passes it asinput
and rereads those rows without asking the outside system again. An
aggregate's groups get a handle too, so they can be filtered and sorted.
Kept rows last 10 minutes; a session keeps up to 64 sets. A full server drops
the busiest session's oldest set instead of refusing, and another session
cannot read the rows. Before, rows were kept only after more than 20 outside
calls or a cut reply, for 5 minutes, 4 per caller. - One handle covers a run of one-at-a-time calls. When an agent asks an
outside system one value at a time, from the third call the reply names one
handle for all the answers and two ready calls that total or rank them, with
no new outside call. mapadds computed columns. An agent can have the server compute a
value for each row of a live result, such asfill = used / total, then
filter, sort or total by it. Stored (non-live) records come in a later
release.- Totals over a cut-short answer say they are short. When an outside system
sends only part of an answer, a count says it "is at least the value shown",
and a sum, minimum or maximum says it is over the rows received only. Before,
such a total read like a whole total.
Clear refusals
- A failed read of an outside system says so, and "nothing matched" is an
answer. When a live read fails, the reply says so on its first line and is
marked as an error, with the remedy. Before, a failure could sit inside a
normal-looking reply, and an agent could take it for an answer. When the
outside system answers that nothing matched or that a name was not found,
the reply is now zero rows with the system's own words, not an error. In a
one-value-at-a-time call, a value with no match gives zero rows for that value
and the values that did answer keep their rows. A real failure still fails
the whole call. - A word search over whole records no longer claims "complete" for text it
never read. When the default search profile lists a document's text chunks
but not the document,searchwithmethod: "grep"andwithin: "record"
read no documents and answered "0 of 0, complete", though a search by text
unit found them. It now reads the documents those chunks belong to and says
so. A filter that names a chunk type next to other types is refused with two
fixes that both run; before, it returned only the other types and said
everything was shown. Chunks nested inside chunks are not searched. - Kept live rows are checked on every call. A later read of kept rows
checks the reader's permissions again, as the first read does. Amapor
filterthat names a field the kept rows lack now fetches it, or is refused
asfield_not_fetched; before, it returned empty values with no error. An
inputsent as a JSON string is read like the object form; before,
aggregateon a live type returned zero groups without an error. - A refused
mapkeeps the rows it fetched. When amapthat adds
computed columns must first fetch missing fields and is then refused, the
fetched rows are kept under a handle and the refusal names it, so the
corrected retry asks the outside system for nothing. Before, the rows were
thrown away and a retry repeated the same outside calls, up to 1,500. The
handle counts toward the caller's own limit and can push out the caller's
oldest handle, never another caller's. - Calling a tool the server does not offer is refused as
tool_not_served,
with the tool name and the list of tools it serves. Before, it looked like a
crash. aggregatewithinputset to the textnullis refused as a bad
argument that namesinput. Before, it failed with an internal error.
Ingest and publish
- A link to a record that is not there yet no longer refuses the run. When
one source points at a record another source has not written, or has since
removed, the pointing source keeps its other records and the link waits in a
hidden table until the record exists.okg doctor --check deferred-edges
reports waiting links, and `okg runtime deferred-e...
AKMON v0.3.0
AKMON 0.3.0 follows 0.2.0 with fixes for the first things a new user hits
(unreadable files that were accepted without a word, PDFs that held only a file
name, a long install printout, a quickstart that built a large image), one
security fix, and the fixes merged since 0.2.0. If you run 0.2.0, see
"Upgrading from 0.2.0". AKMON's Python package and command are named okg, so
every command below starts with okg.
Get it
- Python package: the wheel
okg-0.3.0-py3-none-any.whlis attached to
this release. Install it withuv tool installorpip install, then
follow the quickstart (docs/start/quickstart.md). - Containers and Kubernetes: the Install section at the end lists the
runtime and PostgreSQL images and the Helm chart, each with its digest. The
chart names both images, so an install needs no image settings
(docs/operate/deployments/kubernetes.md).
What's new
First-run fixes
okg deployment add-sourcewarns when a folder holds files AKMON cannot
read. The preview and the result name each unreadable file type with its
count, list the formats read today, and say what to do. When none of the
matched files can be read, it refuses instead of publishing records that hold
only file names.- PDF text is searchable. A local folder source reads the text of each PDF
page by page, so a search for a word inside a paper finds it, with the page
number. It reads the text layer only (not scanned pages, figures or tables),
up to 50 MiB and 5,000 pages per file. The preview warns about a PDF with no
text, such as a scan, and about a PDF that will not open; a file over the size
limit is recorded with no text and a warning. With the default settings,
reading PDFs makes no model call. See "Breaking changes" for which graphs
start reading PDFs. - The README is now short. It says what AKMON is (a consistent, versioned
knowledge base of an organization's data, read by people and agents), the
problem it solves and the pipeline in one line, then points to the
quickstart and the documentation. - GitHub sources can import discussion.
include_discussion=truenow
publishes issue and pull request comments; before, every import with it was
refused. The new optional settingcommit_reponames the git repository in
the same graph and links merged pull requests to their merge commits. It
accepts a git source that comes from acode_reposblock, and refuses one
that is switched off or missing, with the fix. Withoutcommit_repo, merged
pull requests import unlinked and the import step says so. - The quickstart installs a tagged release and starts the published database
image instead of building one, on Docker or podman. One variable,
AKMON_VERSION, picks the release.OKG_PG_IMAGEnames the database image
for the bundled Compose file, so two checkouts no longer retag each other's
image. - The quickstart and the first-run troubleshooting page now cover the gaps a
first-time reader hit on a cold run: a database check that could wait
forever, the podman error about the limit on kernel keys (the fix is
keyring = falseunder[containers]incontainers.conf), the line
search profile: none (unscoped)in search output, and what to do when none
of the four supported agents is installed. okg installends with a ten-line summary: the graph's name, how many
records and links it holds, where the database is, one search to try, and how
to connect an agent (with the extra step for a database on another host).--verbosebrings back the full report;--jsonhas the same
keys as before. In an activated virtual environment, hints sayokg, not
uv run okg.okg agent connect --agent claude-codeworks on a machine where theclaude
command is not installed: it writes Claude Code's configuration and tells you
to install Claude Code before you start a session.okg jobs status,okg jobs enqueue-source,okg jobs find-source-runand
okg chat source-retryprint a one-line error that names--deploymentwhen
you give no deployment or database address, not a Python traceback.- When a source fails its health check,
okg ingestandokg installnow name
the source and the connector's own reason, for example a missing credential.
The message says whether the other sources were published without it or not
published, and gives the command that leaves the failed source out for one
run. That note about the other sources appears whenokg ingestruns in the
same process, not yet when the work is queued (see Known issues). A failed
okg installnames the cause instead of the tail of a log. A successful
publish no longer warns that there was nothing to rebuild. okg install --forceover an existing database, when its final publish is
refused because the database still holds the files claimed before the
reinstall, now says so, points tookg status, and names the two commands
to run next in order:okg catalog ownership claim, thenokg run --once.
It still exits 1 and never claims ownership itself.okg chat syncno longer says that an existing chat preset cannot be
updated. Sync does update it; each census line is now labelled as a note,
not a write failure.
Security
- Error and health messages, and bug reports posted to GitHub, now hide more
kinds of secret: signed-URL values (such as?sig=),Cookieand
Set-Cookieheader values,auth=values,Authorizationcredentials
(Bearer, Basic, token, Digest and Negotiate) and token or password
assignments. A bug report is refused before it is posted if any such value is
left. A large slowdown when masking very long text is also fixed. - When a deployment's access rules are on, a live read from an outside system
(expandwithlive) is now refused by name before anything leaves the
process. See "Breaking changes".
Agents get clearer answers
- A lookup in a live service that finds nothing says "not found", not that the
service is down. A cut answer from a live service says where it stopped and
how to ask for more, never "all shown". - A search over file names says that file contents need a different search.
- Searching for the exact name of a record lists that record first, marked
match_kind: exact_name. This covers the name of a class, function or
method, and the name of any other record, such as the release
CMSSW_14_0_0. Before, look-alike records could fill the top places, or
only imports and references came back and the definition was missing.
Imports and references that repeat a symbol's name follow its definition. Up
to 50 same-name symbols are listed and counted exactly; more is a lower bound
that says why. Under enforced access rules the count is never called exact.
On a graph of 635,000 records the extra read added at most 28 ms to a
search. Only a name that looks like code takes this route (see Known
issues). - When an agent calls
inspectwithkindandnode_idat the top level
instead of insidetarget, the refusal now shows the corrected call, ready
to send, with other arguments such asselectandmax_byteskept. The call
is still refused, not served. - A reply that goes on in another call prints a short 40-character handle for
the next page. A refused handle names the call that restarts the read. A
handle lives in the server process that issued it: it ends at a restart,
after 2 hours, or when the server drops it to stay inside its limits. With a
shared signing key set in the environment variable
OKG_MCP_CONTINUATION_SIGNING_KEY, the reply prints the full token, which any
server process can open. okg inspect textsno longer prints a stray code fence in the middle of a
word when a page holds two pieces of one passage.
Access rules
- With per-user access rules on, keyword search applies the reader's rules
before its read limits, so records a reader may not see cannot fill them. It
also remembers each reader's access decisions instead of checking them again
on every search; the cost measured at about 3 ms per search. Servers where
each request names its reader,greplimits, relevance scores, search by
meaning and records with no read class are not covered yet (see Known issues). - Under access rules, keyword search for one kind of record, such as documents,
keeps using the text index instead of a slower route the database wrongly
picked. On one test graph the database time fell from 183-221 ms to 85-104 ms
with identical results. Four other reads still use the plain filter.
Reading data that lives in another system
- Data held in an outside system can now be read like ordinary graph
records, without storing it. A deployment declares a live type once: its id
prefix, its fields, the outside calls that fill them, and its links. Agents
then use the usualfilter,map,aggregate,inspectandexpand
tools on it. The server plans the outside calls, sends the conditions the
outside system can apply to it, fetches only the fields the call needs, and
applies every condition itself before answering. - A read that cannot work is refused before any outside call: a misspelled
field, metric, grouping or link, a missing argument, an unsafe value, a
plan over 1,500 calls, or a pinned graph version on a live type. Text from
the outside system is marked as untrusted before an agent sees it. - Each live answer starts with one line naming the system, the read time, how
many records were read and listed, the calls made, and the conditions the
outside system applied. A read that made more than 20 calls, or whose
answer was cut, is kept in the server for 300 seconds, so the next call
reads it without calling the outside system again. - A new tool,
run, chains reads in one call: live reads, value lists,
differences and overlaps between result sets, and totals over the rows it
holds, with o...
AKMON v0.2.0
The first public release of AKMON. It builds a searchable knowledge graph
from your repositories, documents and services, and serves it to coding
agents (Claude Code, Claude Desktop, Cursor and Codex) over MCP, the protocol
those agents use to call tools. AKMON's Python package and command are named
okg, so every command below starts with okg.
Get it
- Python package: the wheel
okg-0.2.0-py3-none-any.whlis attached to
this release. Install it withuv tool installorpip install, then
follow the quickstart (docs/start/quickstart.md). - Containers and Kubernetes: the Install section at the end lists the
runtime and PostgreSQL images and the Helm chart, each with its digest. The
chart names both images, so an install needs no image settings
(docs/operate/deployments/kubernetes.md).
What's new
Agents get better answers
- Search finds exact names, ticket keys and file names, and lists a file
first when you name it. Duplicate records no longer crowd out results. - Search results show a heading and the matching passage, not the whole
record, and a record already shown in the session is referred to instead
of repeated. Searches return 8 results by default. - Search and record replies say on their first line how large they are and
which setting makes them smaller. A reply that would be too large comes back shortened, with the
call that reads the rest, instead of being refused. - When a call is nearly right, AKMON answers it and says what it changed;
otherwise it says what to change. A mistyped record id gets the nearest
real one. Results say what they left out and where to find it. - A read never says it is complete when it left rows out, and a condition
with a value of the wrong type is refused instead of answered with an
empty page.
More ways to read the graph
- Every read tool takes the same conditions, including dates, text patterns
and an accent- and case-blind substring test, and the same field names. - Read up to 50 records in one call, read chosen lines of a text, and count
records per day, week, month or year, per list element or per link. expandreturns the records a record links to, each with its type and
title;*follows any link.- Exact text search can list every matching record with an exact total and
gives the line number of each match. - The read-only SQL tool
querydescribes the graph it serves and pages a
large reply. It is offered when per-user access rules are off. - Search by meaning (optional). With vector sets configured under
embeddingsindeployment.yaml,searchcan rank records by meaning as
well as by words (docs/operate/configure.md, "Embeddings").
Reading from a shell
- The
okgcommand line has one command per read tool:okg search,
inspect,expand,filter,aggregate,mapandquery. Each prints
the reply the MCP server would send, or JSON with--json, and exits 0
when served, 2 when refused and 1 on any other failure. - The commands take short shell forms (
--where status=Open, a bare record
id) and print rows for a pipe (--jsonl,--ids,--all); ids can be
piped from one command into the next. okg <command> --helpshows each tool's own description with a worked
example, andokg guideprints the instructions an agent gets when it
connects.
Larger answers and live services
- An agent can page through, filter and count one large answer from a live
service (a database or API the graph connects to) without running out of
context. - An agent can keep a search result and come back to it later in the same
session. - When an agent connects, a short summary tells it what the graph holds.
- Live services that answer with a single object return it as one row.
Many agents, many users
- One server handles several agents at once without queuing them.
- With per-user access rules on, each reader sees only what they may read:
search ranks only readable text, and a record a reader may not see reads
as not found.
Setting up
okg installsets up a graph in one command;okg agent connect
connects every agent it finds.install.shinstalls from a clone.- The bundled database's port, container name and password are settings,
and it runs under its own Compose project,okg-pg. okg doctorpasses on a fresh install without false alarms, checks that
each agent's saved command still exists, and fails when the newest publish
was refused.- An unreachable database is reported in one line that names the host and
the setting to fix. - When an agent can't find the database address,
okg agent connectsays
where it must go and prints the command that puts it there. - The "Run it as a service" docs lead from a working graph to a shared
service in one path.
Keeping a graph current and safe
okg upupgrades an installed graph: it checks compatibility, backs up the
database and checks the backup, migrates, and writes a record of what it
did.okg deployment backup restoreputs the backup back.- A publish blocked by the catalog's rules names the missing rule and the
module that provides it. A publish check can name the sources it depends
on; while one of them is off, the check is skipped and reported instead of
blocking the publish. okg doctorwarns when a source loses most or all of its records, and
reports a publish schedule you paused on purpose as paused, not stale.- Credentials that appear in stored Git diffs are hidden before they reach
the graph, and records of deleted files say when and by which commit they
were deleted. - Git fixes: each commit carries its author as plain fields; turning rename
detection on or off applies to history already read; a file that changes
type is one change; a branch switch with no file changes moves the files to
the new branch; an unchanged run no longer removes file records. - Undoing a source run no longer deletes a later rerun's rows.
- The outbound-fetch allow list accepts IPv6 addresses.
Breaking changes
These change how an existing installation behaves. A new install is not
affected.
- Migrate each database once. After installing 0.2.0, run
okg up <deployment>(orokg migrate --deployment <deployment> --apply)
for every existing graph; the steps are below. A graph pinned with
release.channel: prodalso needs itsrelease.pinindeployment.yaml
updated to the 0.2.0 values; check it with
okg deployment release <deployment> --json. - Move keyword search to pg_textsearch 1.5.1. Existing databases keep
version 1.3.0, which can repeat or drop keyword-search results, until you
move them by hand. The move is one-way, so back up first; see below. - Unknown source settings are refused. A source registry file
(source_registry.yaml) now refuses settings AKMON does not recognise; the
error names the setting and the closest valid one. Rename or remove it
before upgrading. - A sync that suddenly returns nothing now fails. When the background
worker reads a whole source and gets no records while the graph still holds
records from it, the sync fails on every check instead of quietly keeping
the old records. If the source really is empty now, run
okg ingest --inline --allow-full-retraction <source>once. - A failed source no longer blocks publishing. A published version can now
hold fresh data from the sources that succeeded next to older data from one
that failed, and itsingest_completenessmarker says which.okg migrate
lifts the repair requirement the earlier behaviour left on such versions. okg catalog load --applywaits for, and blocks, other writes. It now
holds the graph's write lock while it runs, so other writes to the graph
wait until it finishes.- Read replies changed shape. Scripts that parse replies should check
three changes: a one-hopexpandreturns one row per linked record (select
only link fields for the old one row per link); searches return 8 shorter
results by default (setlimitandmax_charsfor more); and under
per-user access rules the SQL toolqueryis no longer offered.
Upgrading from an earlier build
Only if you ran AKMON from source before this release:
- Install the 0.2.0 wheel, then run
okg up <deployment> --planand
okg up <deployment>.okg upneedspg_dumpandpg_restorefrom
PostgreSQL 17 on your PATH;--planchecks for them. - Move keyword search to pg_textsearch 1.5.1 (required). Back up,
restart the database on the 0.2.0 PostgreSQL image (the published
akmon-postgres:v0.2.0image in the Install section, or a rebuild of
ops/pg), then run
ALTER EXTENSION pg_textsearch UPDATE;in each AKMON database. Version
1.3.0 can repeat or drop search results.okg doctorwarns until this is
done. The move is one-way; going back takes the backup.
Seedocs/operate/lifecycle.md, "Upgrade to 0.2.0". - Expect one long Git sync. The first sync after the upgrade reads
every Git commit again once, to store each commit's author. On a graph
with a long Git history this took 6 to 7 minutes, then a publish of 8 to
10 minutes, and the database grew by about 3 GB.
New installs on the 0.2.0 image need none of these steps.
Known issues
- Code parsing on a slow network. On first use, code parsing downloads
about 500 MB of language grammars, and the download stops after about a
minute. On a slow connection, download them in advance; see
docs/runbooks/troubleshooting/first-run.md. - GitHub comments. The
githubsource'sinclude_discussion=true
setting fails the import (producer_node_subtype_not_allowed); leave it
off. Issues and pull requests themselves import normally. - Upgrade, restore and copy need PostgreSQL client tools.
okg up,
okg deployment backup restoreandokg db cloneneedpg_dumpand
pg_restoreat the database ...