Repository navigation
v0.6.9
⚠️ Upgrading
- No migration, no new setting and no change to
docker-compose.yml. Data written by 0.6.9 reads in
0.6.8, except that a history source that stored/docsfails again there. - Uploads are bounded in more ways, and some answers change.
- A request is read for at most ten file parts and 100 multipart sections, form fields included.
Past either limit the application reads no more and answers202with a request-levelfailed
entry (fileisnull); parts it did not read are in neitherstorednorfailed, so a script
readsfailed. When none of the first ten files was stored the answer is400"No files could be
stored", or400"No files in the request" (its detail repeats the text only when the section
limit was hit), or503when at least one failure was server-side. The transfer is not stopped,
so no bandwidth is saved. - A file name is refused on upload (listed under
failed) and on attach (400"Invalid file
name") when it holds a control character (line breaks, U+2028 and U+2029 included), a
bidirectional override or isolate (U+202A to U+202E, U+2066 to U+2069), or is longer than 260
UTF-16 units. U+200E, U+200F and U+061C pass. Attaching with a blankfileNameanswers400;
leaving it out keeps the name the document was uploaded under, and400follows if that name
breaks the rule, so a script that attaches by hash alone gets400for such an older document. DEXICON__INDEXING__EXTRACTIONTIMEOUTSECONDS(300; 0 disables) applies to uploads as well. An
upload that exceeds it, or fails for a reason that is not a verdict on the file (an I/O error,
a refused permission, a shortage of memory, aTimeoutException, a library cancellation), is
listed underfailedand no document record is created; the bytes stay in/data/blobs, up to
DEXICON__UPLOAD__MAXFILEBYTESeach, listed nowhere. Sending the file again extracts it again
for the full budget, with noRetry-After. A concurrent upload of the same bytes is reported as
stored.- A
blob_textsrow written by 0.6.8 or earlier for one of those failures keeps its message as
the document's empty reason. It is at the current extractor version, so no pass extracts it
again, and uploading the same bytes returns it as already stored. Nothing in the product clears
such a row; a later extractor version extracts it again.GET /api/documentslists the reason
of each document that has no text. - A DOCX, PPTX or EPUB package with an XML part nested deeper than 512 levels, or XML that
inflates to more than 256 MiB in total, is an unreadable document. An HTML file or EPUB chapter
with more than 1,000,000<characters is too. The same limits apply to workspace files: a new
or changed file over them is an unreadable document, and a file already read is not read again.
HTML has no nesting limit: its parse is cancelled when the extraction clock runs out, so a
document too deep to parse within the budget is a timeout, and with a budget of 0 the HTML parse
is unbounded in time.
- A request is read for at most ten file parts and 100 multipart sections, form fields included.
- Creating a corpus checks its chunk settings. A client that posted a size outside 64 to 8,192,
an overlap that is negative or not below the size, or an unknown boundary mode to
POST /api/corporaand relied on201gets400, andcustomcannot be set at creation. A
server that setsDEXICON__INDEXING__CHUNKSIZE,CHUNKOVERLAPorBOUNDARYMODEoutside those
rules logs an error at startup and answers503to a creation that does not send its own value
for the setting that is out of range. A size under 64 or over 8,192, an overlap equal to the size
and a negative overlap used to create corpora that indexed; an unknown mode used to create a
corpus whose code and text files all failed.docker-compose.ymldoes not forwardBOUNDARYMODE;
set it indocker-compose.override.yml. A chunk set added from a template inherits the template's
size and overlap as stored, even when the current rules would refuse them. - Glob lists are capped, and git pathspecs are checked at save. A list holds at most 200
elements and an element at most 500 characters. A history source's include list, and a corpus
default include list (which history sources inherit), are refused when an element makesgit log
fail on every pass: an empty element, an element holding a null character, malformed pathspec
magic, a path that climbs out of the repository, a rooted path, or an element starting with/:;
the rules are those of git 2.54.0 on Linux. The web UI forms andconfigure_corpussend stored
lists back, so a stored list that fails a rule blocks any save that sends it until it is edited or
reset (resetinconfigure_corpus). That includes a default over the caps and one stored with
an empty element,//x,/:x, a climbing path, a rooted path or malformed magic, all of which a
file source read without error. No stored row changes. A history source that stored/docsfails
again on an older version. - Ignore files are read with limits, and a breach fails the source. A walk reads at most 5,000
rules and 60,000 pattern parts across all ignore files, a file of 1 MiB, and 16 MiB of ignore
files. A stored list is held to 1,000 rules and 20,000 parts. Passing any of these fails that
source's walk, for.gitignoreand.git/info/excludeas well as.dexiconignore: the source's
files are not indexed or removed, the corpus reads unavailable and the reason is in
index_status, naming the files that used most of the budget and, when a.gitignoreused some
of it, offering to turn offuseGitignorefor the source (in the web UI and over REST; MCP keeps
it on). A line or list entry over 500 characters fails the walk in a.dexiconignoreand in a
list, and is skipped with a warning in a.gitignoreor.git/info/exclude. A stored list with
an entry over 500 characters, a POSIX class, more than 1,000 rules or a weight over 20,000 makes
its source unavailable on the first pass after the upgrade, until the list is edited. A
.dexiconignorethat has an unusable line, a carriage return inside a line, a link, a pipe, an
unreadable file, a NUL, bytes invalid in its encoding or a twin differing only in case fails the
walk. Before, a link was skipped, a NUL or an invalid byte was read as a character that matched
nothing, and a lone carriage return ended a line, so a file with old Mac line endings applied and
now fails the walk; lines end at a line feed, as in git. A.gitignoreor.git/info/exclude
with CR-only line endings is read as one line, matches nothing and raises no warning; one that
holds a NUL is skipped whole with a warning, where only the NUL line was inert before. Files they
excluded are indexed on the first pass. Ignore files are found by name without regard to case on
every platform: on Linux a.GitIgnoreor.DexiconIgnorethat was ignored before is now read.
Byte-order-marked UTF-16 and UTF-32 files are still decoded. - Bracket classes and some patterns are read as git reads them. A class written with a leading
!, such as[!a]*.md, matches any character except those listed. No class matches/; a
positive class that spanned it, such as[+-9], matched it before. A backslash inside a class
escapes the next character ([\d]is the letterd, not a digit), and[a-z-[b]]is the
membersa-z,-,[andbfollowed by a literal], where it was class subtraction. A
leading], a[and a trailing-are members.
POSIX classes, any other name between[:and:], empty classes and reversed ranges ([z-a])
are refused as unusable lines. Three or more stars forming a whole segment are one**. Lines
are split at line feeds. On the first pass after the
upgrade, a source whose patterns change meaning indexes the files it now selects and drops the
ones it no longer does. Differences from git remain and are listed indocs/04-ingestion.md. - Removal, attach and promote answer differently. The second of two simultaneous removals of one
source, chunk set or corpus answers404("removed while this request waited"). A laterDELETE
of a source or a chunk set answers404, and a laterDELETEof a corpus answers400from the
scope resolver. An upload or attach that waited behind the removal of its corpus answers400as
an unknown corpus. A chunk set that became the default or the only set while its vectors were
being deleted is kept and the removal answers409; a refresh of the set is queued, and a
proposal to remove it stays waiting. - The embedding model probe needs the admin session.
POST /api/embedding-models/probeanswers
403to a key, whatever its scopes; 0.6.8 accepted a key withsearch. - Log lines and tool replies change. Characters the console template leaves alone are replaced
with U+FFFD in logged values: DEL, C1 controls, line and paragraph separators, bidirectional and
other format characters, and a zero-width joiner or non-joiner, so a family emoji or a Persian
word written with a ZWNJ shows the marker. A line break in a logged value shows as\ninside
quotes. An exception is written a line at a time: each line is cut at 4,000 characters, the rest
after the line that crosses 64,000 (about 64,000 in all) and a string property at 8,000, and a
line the runtime did not write is indented by four spaces. MCP tool errors show a caller's value
on one line cut at 200 characters, stored text on one line cut at 4,000, only three unknown corpus
names and a corpus list cut at a whole name within 1,500 characters; thedetailof the REST
400for an unknown corpus is cut the same way. An agent no longer sees a provider's message or a
path the workspace resolver refuses; the log has them. - The skill changes (
dexicon-skill-version: 7). It tells the agent thatindex_statusshows three of
the five common reasons for an empty result (still indexing, never indexed, left out) and describes
the ignore-file failures. The tools and hooks are unchanged. Runningscripts/install-mcp.ps1
again upgrades installed copies.
Fixed
- Uploads. Extraction of an uploaded file has a time limit. A stored document whose
re-extraction fails keeps its previous text and is tried again once per job, then chunked again
after a later success even when another job rewrote the row meanwhile. A damaged EPUB entry, an
I/O error reading an upload and a fault inside an extractor end in a stored document with a
reason or an entry underfailed; they ended in "The multipart body could not be read" and lost
the files after them, or replaced a stored document's text. Deeply nested HTML, DOCX, PPTX and
EPUB documents no longer end the process. A workspace extractor that caught the timeout and
returned partial text has that text discarded, not cached. Attaching a stored document with a
blankfileNamestored the blank as the document's path. - Attach, detach and removal.
- Removing a source, a chunk set or a corpus takes the attachment lock for its catalogue delete,
after its vector calls, and reads its target again under it, so an attachment is saved against
rows that are there; an attachment reads the corpus's chunk sets under the lock and its
response lists those sets. Promoting a chunk set takes the same lock, so a removal cannot delete
a set that has just become the default. The lock does not cover adding a source or a chunk set;
a removal of the corpus at the same moment fails their save with a foreign-key error. - A document detached while an indexing pass runs is left out of the pass's saves, so the pass
completes and the job is recorded. The saves that insert chunk states, claim a document, flush
after one and end the source delete the vectors the pass wrote for it and count it as skipped;
the count reconcile and the save that records the outcome only leave it out, and a later pass
of the same chunk set removes its points. When rows a pass holds are deleted the job is
recorded: a document or an upload source givesSucceededwith the documents skipped; a folder
source, a chunk set or a chunk state givesFailedwith "A chunk set, source or document was
removed while the pass ran."; a deleted corpus leaves nothing to record and logs two warnings.
A job whose own row is gone is not recorded and its save is not retried. - Detaching a document deletes its vectors again after its row. That delete is best effort with
a 30 s limit of its own and a failure is logged; a later pass removes the points. Points a
running pass writes for a source after a removal's vector deletes stay in the vector store. - Two simultaneous refresh requests for one corpus queue one job.
- Removing a source, a chunk set or a corpus takes the attachment lock for its catalogue delete,
- Glob and ignore patterns. A glob list with a null element, or a pattern that does not
compile, was stored as sent and failed every pass of every source that read it; saving refuses
it, with400naming the list and position (includeGlobs[1]) and the same text from
configure_corpusandconfigure_source.PATCHon a source with"clear": [null]answers
400"Unknown filter" instead of clearing nothing. A history source's include/docsselects
the same commits asdocsand is not re-read when one is edited into the other. Git's matching
differs from the file walk's: an unglobbed name is anchored at the root,*crosses/, and
**/*.mdneeds a/unless it is written:(glob)**/*.md; theconfigure_corpusand
configure_sourcedescriptions say so. Git calls no longer inherit anyGIT_variable except
the two the code sets. The web UI forms split a path list at commas and newlines except inside
the parentheses of pathspec magic, and the history editor sends its paths only after they are
edited. - A chunk set that could not walk a source is not promoted. Promoting a set whose last pass
could not walk a source (an unusable.dexiconignore, a limit passed, a missing root) answers
409"Chunk set is unavailable". Only pending files were counted before, so the set became the
default and search lost that source's files. index_statusnames the directories its coverage check could not read. A directory whose
ignore file is unusable is listed asCOVERAGE NOT CHECKED: <directory>, because <reason>, up to
five and then a count, where it was left out of the "NOT INDEXED" advisory without a word. The check
applies a.dexiconignorein the directory as well as a.gitignore. The web UI's coverage endpoint
still leaves such a directory out, and logs the reason at Information.- Ignore matching. Ignore patterns are matched by tokens over bit sets of path positions, with
no regular expression and no match timeout. A match takes at most
(tokens + 1) × words + classes × (length + 1) + lengthsteps and the longest literal run of a
pattern is searched in linear time, so a pattern such as*a*a*a*a*bor ten**/in a row
cannot stall a pass. A.gitignoreline that cannot be used is skipped with a warning; after
1,000 skipped lines nothing later in that file applies and the log says so. At most 20 warnings
and 20 of those notices are kept and the rest are counted, once per pass. The message for a walk
past its rule budget offersuse_gitignore: falseonly when a.gitignoreor
.git/info/excludehas used part of the budget. A.gitignorein a directory the rules already
ignore is not read, so a vendored tree does not use up the rule budget; a.dexiconignorethere
is read, and so is any ignore file in a directory a negation brings back. On Linux and macOS ignore files are opened non-blocking and refused if the
handle cannot seek, so a pipe cannot hang the walk. A message that quotes an ignore-file line is
cleaned of control, format and separator characters. - Chunk settings, logs and tool replies.
POST /api/corporaapplies the chunk-set rules to
the corpus's first set: a bad request value answers400before the embedding probe and saves
nothing, and a request value that fails only beside a usable server default names the default.
Editing a chunk set judges each setting the edit changes, and size and overlap together when
either changes; a value sent unchanged is not a change. The corpus dialogs do not suggest a chunk
size over 8,192 tokens or under 64, and the edit dialog sends only the settings that were
edited. A log line cannot be forged with a corpus name, a path, an exception or a message
template; a chain deeper than 100 levels or longer than 1,000 exceptions is written as its
outermost type and message with a note, dictionary keys that collide get a counter and an entry
that cannot be made safe is replaced by a line saying it was withheld. A failure in
InitialiseAsyncor while running is logged through the same sink. Afterconfigure_corpushas
saved a corpus, a later failure is logged and the agent is told only that the corpus was created.
A null entry inresetis refused with a message. - The web UI sends a drop of more than ten files as consecutive requests of ten, and stops at the first
request that fails with a401,403,5xxor no answer, listing the rest as not sent.
recommendedChunkTokensin/api/embedding-modelsand in the probe result is limited to 64 to 8,192. - Detaching a document while the same document is being attached does not fail the attachment's
save, and a later file of an upload batch that failed with an unexpected error leaves the earlier
files with an indexing job.configure_sourcewith a folder holding a null character answers
with a message naming the character. The unknown search mode echoed in an error is cut to 40 characters, with line breaks shown as a space
and any other control character replaced by U+FFFD.
Known limits
- A PDF whose page tree is nested a few thousand levels deep (3,000 to 7,000 in probes, by file
layout and platform) ends the process when it is opened. A real bound needs PDFs opened in a
separate process. - An HTML or EPUB parse is bounded in time by the extraction clock and in tags by the 1,000,000
limit; its memory per tag was measured at 290 to 560 bytes. - A kept chunk set whose follow-up refresh is lost to a cancellation or a failed jobs insert stays
empty until the next refresh;DEXICON__INDEXING__REFRESHMINUTESis 0 by default. - A directory that cannot be listed reads as empty during an ignore-file walk, and a hard link to an
ignore file is read as the file it links to. - A request refused with
413, or past the file or section limits, is still read off the connection to
its end: no cap was found up to 2,500 MB, so the request bound does not limit bandwidth. The XML scan
holds one attribute value or text node whole, about 4 to 5 times its inflated size (about 1.1 GB at
the 256 MiB budget). - A chunk set promoted before its first pass has run has no pending files, so it is promoted and
search finds nothing in it until the pass finishes. Promotion is refused while files are pending
and when the set is unavailable. - A
.gitignoreof 1,000 unusable lines costs about 1.6 ms to read, so a tree of thousands of such
files makes each pass spend seconds in the walk (300 files: about 0.5 s). - A
.dexiconignoreline that holds a zero-width space (U+200B) or a byte order mark (U+FEFF)
inside the line matches nothing and does not fail the walk. A directory named.dexiconignore
applies no rules and does not fail the walk, as before this release.
docker pull ghcr.io/merp4/dexicon:0.6.9
Digest sha256:0a8445e0fa0c7ca019795d6ac6f3e599bcd732ce623d92b52db62307f2423ce5. An SBOM and build provenance are attached to the
image in the registry.