Repository navigation
Releases
Korely Agents 1.0.26
1.0.25 with the old secret of a rotated key holding no admin, every
request that comes during the proxy's first name lookup waiting for it, and
a check of the public copy for lines that name what only Korely Cloud has.
From 1.0.25, the one thing to look at is the old secret, in "Before and
after upgrading" below; from 1.0.22, read the 1.0.25, 1.0.24 and 1.0.23
entries too.
Before and after upgrading
- No migration and no new setting.
- The old secret of a rotated key holds no
admin. During a rotation's
overlap, a script that still sends the old secret to an/adminroute
gets 403previous_secret_has_no_adminand nothing changes; one that
erases with it an end user to whom a key is confined alone (DELETE /v1/users/{id}/memories) gets 403previous_secret_cannot_manage_keys
and nothing is erased. Send the key's current secret, the one the rotation
answered with. Every other call of the key's scopes goes on working with
the old secret until it ends.
Security
- A leaked key's holder no longer has
adminwith the old secret.
During the overlap (old_key_expires_in_hours, 24 by default) the old
secret of an admin key was the key in full: its holder could rotate the
key again with no overlap, end the new secret its owner had just taken
and receive the newest (the security review of 14 Nov 2026), delete a
project, which moves its keys, or set the model provider's key, which
outlives the overlap (the skeptic's review of 15 Nov 2026). Now a request
let in with the old secret holds the key's scopes butadmin, as a key
Stripe rolls keeps working for API calls (https://docs.stripe.com/keys):
every/adminroute refuses it as it refuses a key withoutadmin, so
it makes no webhook either (those made before stay), and an erasure that
would revoke a key as it goes is refused to it. A rotation let in with the
current secret a moment before the owner's own committed reads the key
again under its lock, and is refused too. The key's current secret, and a
person signed in to the dashboard, do all of it as before. For a key that
leaked, rotate with"old_key_expires_in_hours": 0, or revoke it
(Rotating a key). - The proxy's requests during the process's first name lookup are the
proxy's. Up to 1.0.25 only the request that started the first lookup of
the names inFORWARDED_ALLOW_IPSwaited for it: a request of the proxy
that came while it ran was served with the proxy's address andhttp, as
one relayed by a stranger (one login lock and one cap per network for
every client relayed so,/healthwithout its details), and the
one-time[proxy]warning said, wrongly, that the proxy was not trusted
(found on the Cloud's deploy of 14 Nov 2026). Now every request that
comes before the first lookup has answered waits for it, until 2 s after
it began; a lookup that fails is no answer, never an error of the
requests waiting for it; and the warning is said, once the names have
answered, of any peer seen meanwhile that they leave out.
What is different
docs/guide/operations.md, "Your own proxy in front", says again that a
proxy of yours keeps the paths it logs, which carry end users' ids, and
saysPUBLIC_BASE_URL, which only.env.examplesaid.
API
- Every
/adminroute: 403previous_secret_has_no_adminfor a request
let in with the old secret of a rotation; their 403 in the OpenAPI says it. DELETE /v1/users/{id}/memories: 403previous_secret_cannot_manage_keys
for the old secret of a rotation when the erasure would revoke a key.GET /v1/pinglists the scopes the secret sent holds: the old one's
withoutadmin.
Tools in the repository (nothing to do for an install)
- The checks a public copy of this repository passes before it is
published also fail on a line that names what only Korely Cloud has,
by the names the code the copy leaves out calls itself and the words a
sentence says them with, a sense of its own a public file gives one of
those words allowed phrase by phrase, with why. A test runs the same check
at the gate, and the ten lines the security reading of 1.0.25's public
copy found are rewritten. - Two lines of the 1.0.23 entry pointed to notes the public copy does not
carry; they now say what they meant where they are.
Korely Agents 1.0.21
1.0.20 with three comments rewritten, for the public tree: 1.0.20 runs on the
Cloud but never reached the public repository, because the review of its
public copy found comments that named a Cloud feature not out yet. From
1.0.18, read the 1.0.20 and 1.0.19 entries below: that is the upgrade.
Before and after upgrading
- Nothing to do. No migration, no setting, no change in what any route or
tool answers.
What is different
- Comments in the identity rule's code and tests describe the rule without
naming a Cloud feature that is not released.
Security
- No change to who can read or write what: only comments and test
docstrings changed.
Korely Agents 1.0.18
Before and after upgrading
- Nothing to do: no migration, no new setting, and no answer of the API,
the MCP or the dashboard changes.
Security
- Dependencies with published advisories, moved to the first fixed
release (each one is at least two weeks old; nothing else moves):certifi2026.7.22 -> 2026.10.23, in the image: the CA bundle no longer
trusts the emSign roots whose intermediate keys were compromised
(GHSA-6q48-6c7w-9gw2). Korely's calls to model providers and webhooks
verify TLS against this bundle.pyasn10.6.4 -> 0.6.5, in the image: four quadratic-time decodings of
crafted ASN.1 (GHSA-2qm8-x737-gmw5, GHSA-9m75-vc3g-4x39,
GHSA-fm9c-5rcf-2q5p, GHSA-mm57-4v3p-rchx).- The dashboard:
vite6.4.3 -> 6.4.5 (the tool that builds it and its
development server, not part of the built pages), and, through
overrides indashboard/package.json,follow-redirects1.16.0 ->
1.16.2 andre2js2.8.6 -> 2.8.8. scripts/diagrams(the tool that draws the README's diagrams, not part
of an install), through overrides:nanoid3.3.3 -> 3.3.18 and
4.0.2 -> 5.1.16,lodash-es4.17.21 -> 4.18.0,katex0.16.47 ->
0.18.2.
docs/DEPENDENCIES.mdlists the new versions.
Tools in the repository (nothing to do for an install)
scripts/release.shrefuses a release whose CHANGELOG entry is not on
top under its own heading (## <version>, with no(Unreleased)),
before it changes anything. The script writesVERSION,
pyproject.tomlandinstall.sh, never the CHANGELOG's text, and
v1.0.17was tagged with## 1.0.17 (Unreleased)on top; the 1.0.17
entry's heading is now## 1.0.17on main (the tag stays as it is).
Tested intests/test_a_release_carries_its_own_heading.py.
Korely Agents 1.0.17
Before and after upgrading
- Nothing to do: no migration, no new setting, and no answer of the API,
the MCP or the dashboard changes.
What is different
- The README says what is in this repository: the memory, projects,
agent notices, the MCP server, the REST API and webhooks, the dashboard
and the benchmark scripts. Korely Cloud runs the same code with sign-up,
plans and payments and spending limits on top, which are not here. - Comments in the code and in the tests that named who reviewed or decided
a change now give only the date of the review or of the decision. - The tests' made-up data no longer copies the rules of the team that
builds Korely (a policy, a deploy procedure, a server), and their made-up
addresses are onexample.testandexample.com. What the tests check
is the same. - memory_core 6.8.1: the same change in two comments of the facts engine,
text only. No code, prompt or printed line of the engine changes.
Security
- No change: only the README, the CHANGELOG, comments, test data and the
test suite change. The suite's test databases are dropped as before, only
after waiting for the sessions that are leaving.
Tools in the repository (nothing to do for an install)
- The test that failed on the CI's runs of 10 Nov 2026.
test_a_workers_drop_ends_none_of_the_applications_connections
(tests/test_the_suite_runs_in_several_processes.py) counted 2 sessions
left open where it had left 1, on main and onv1.0.15, and passed on the
Mac.drop_a_runs_database(tests/conftest.py) closed the pools of the
application's engines and counted the database's sessions on the server at
once; a connection the client has closed stays inpg_stat_activityuntil
its backend process ends, a moment later. With the test and a Postgres of
its own on two processors, as on the CI's runner, it failed in 4 runs of
15 (3 times 1 for 0, once 2 for 1, the CI's message); in 15 more runs with
a probe, the 12 sessions seen right after the close beside the one left
open were all the application's two connections the drop had just closed.
On the Mac's shared Postgres it passed. No thread or engine of an earlier
test was involved: in the suite run in the CI's order on the Mac, up to
this file, the threads left were idle pools, with no connection on the
database. The drop now asks PostgreSQL first for a drop withoutFORCE,
which waits up to 5 seconds for the backends still in the database and
refuses (object_in_use) only when one stays; only then it counts the
clients' sessions (backend_typeclient backend) and dropsWITH (FORCE). A closed session is no longer counted as one left open, and
FORCEno longer ends it, with the "terminating connection" line in the
server's log that the closing was there to avoid. The count leaves out the
other processes in the database: during the longer wait an autovacuum
worker came in and was counted in 3 runs of 10 on two processors, and the
parallel worker of a session's query is that session's. A drop with a
session left open now takes 5 seconds, PostgreSQL's wait: the file's tests
pay it three times, and a worker of a parallel run pays it at its end only
when a test left a connection open. The test also runs with every closed
connection reaching the server half a second late (closed_late), which
fails the old count every time; a session left open in the middle of a
parallel query counts once; and a drop withoutFORCEgiven up on for
another reason (cancelled while it waits) is raised, never forced. On two
processors the test now passed in 15 runs of 15, both cases; it has not
run on the CI's runner yet.
Korely Agents 1.0.11
Before and after upgrading
- On an install of your own, nothing to do and nothing changes: no
migration, no new setting, no API answer of a different shape.
Security
- On an install of your own, the webhooks' address check is the same: it
gains a hook (webhook_destination_problem, core/extensions.py) that
only the Cloud fills, and that does nothing here.
Korely Agents 1.0.10
Before and after upgrading
- On an install of your own, nothing to do: no migration, no new setting,
and nothing in the API changes shape. A client that reads the 401's words
instead of its codeinvalid_keyreads new words, and a key revoked while
its request waits is refused where the request writes (both below).
What the store answers differently
- The 401 for a key that does not authenticate names expiry. Up to
1.0.9 every/v1and/adminroute answered an unknown, a revoked and
an expired key alike with "Invalid or missing API key: no live key
matches it (a revoked key stops at once); your keys are at …/keys", so a
key past its date was told of a revocation nobody made. Now the same
three get "Invalid or missing API key: no live key matches it (unknown,
revoked or expired); your keys are at …/keys". They still get one answer,
and the code is stillinvalid_key: branch on the code, not on the
words. The MCP door (/mcp,/agent/mcpon the Cloud) is not one of
them: it still answers its library's own 401,invalid_token
"Authentication required". - A request that waits before it writes reads its key again.
POST /admin/webhooksandPOST /admin/webhooks/{id}/enablelook the
webhook's address up (up to 5 seconds) before they write, and
POST /admin/keysmakes the key after its other checks. Up to 1.0.9 they
wrote with the key as it was when the request came in, so a key revoked
during the lookup still made or switched on the webhook. Now each reads
the key again in the transaction that writes: revoked or expired
meanwhile, 401invalid_keywith the words above; withoutadmin
meanwhile, 403forbidden. A request made with a dashboard session is
not read again.
Security
- The new words of the 401 change nothing about which key is let in: an
unknown, a revoked and an expired key still get one answer and one code,
so a caller cannot tell the three apart. - A key revoked while its request waited no longer writes with the rights
it had: the three routes above read itsadminagain where they write,
under a share lock of the account's row.
Korely Agents 1.0.9
Before and after upgrading
-
The public repository is now
korely-ai/korely(it was
korely-ai/korely-agent), and its image with it: the image its CI publishes
is named after the repository, so the releases from this one on are
ghcr.io/korely-ai/korely:<version>, and only there. install.sh, the
compose files,.env.example, the README and the guides name the new
addresses; install.sh holds the name once, asPUBLIC_REPO. -
The old addresses still open, through GitHub.
github.com/korely-ai/korely-agent
sends you togithub.com/korely-ai/korely, and the old installer line,
curl -fsSL https://raw.githubusercontent.com/korely-ai/korely-agent/main/install.sh | sh,
downloads the install.sh on the renamed repository's main, the same file
as the new line,
curl -fsSL https://raw.githubusercontent.com/korely-ai/korely/main/install.sh | sh.
This lasts as long as GitHub keeps the old address pointing at the
renamed repository: GitHub drops it if a repository is created again under
the old name. Links and scripts of yours are better on the new address. -
An install already made from the image keeps working as it is. Its
.envnames the image it runs,
KORELY_IMAGE=ghcr.io/korely-ai/korely-agent:<version>(with the digest,
when install.sh had one): that image is on your machine, and the releases
up to 1.0.7 are still published under the old name. No later release is
published there, so that line never brings you a newer one. Nothing to do
now. It is the one line of such an install that names the repository: to
run a release published under the new name, it must name that release
there (put the release in place ofX.Y.Z, in the install's folder):sed -i.bak 's#^KORELY_IMAGE=.*#KORELY_IMAGE=ghcr.io/korely-ai/korely:X.Y.Z#' .envThe old line stays in
.env.bak. -
A git checkout needs nothing:
git pullfollows GitHub's redirect, and
the image is built from the checkout. To name the new address yourself:
git remote set-url origin https://github.com/korely-ai/korely.git.
docs/guide/install.mdnow clones into a folder namedkorely-agent, the
name it had before, so that a checkout keeps its Compose project and is
never the same project as an install.sh install, which is namedkorely. -
No migration and no new setting. The changes to the writers and to the
model settings below need nothing before or after the upgrade. -
Nothing to do for memory_core 6.3.0: no migration, no setting. A reader
of thefact.invalidatedwebhook that matched oninvalidated_bybeing null
for a past statement now receives the replacing fact's id there. -
Nothing to do for memory_core 6.2.1: no migration, no setting; one more
INFO log line, with counts only. -
Nothing to do for the PATCH with the same text: no migration, no
setting. Facts closed that way before this release stay closed.
What the store answers differently
Two words used below. A region is where a model reads the memories of a
project to write its facts: eu (a provider in Europe) or global (a
provider anywhere). The drain is the process that takes the stored memories
from the queue and writes their facts; when it rests a region it does not
try that region again for that time.
-
GET /admin/modelsandPUT /admin/models/{provider}carry
default_region, the region a project that never chose one writes in:
the region of the writer in force, which is the provider saved last on the
screen, else the one.envsets (nullif neither, or if.envpoints to
an address no known provider uses). It says whatactivealready implied.
The default region is read from the same writer as before; nothing is
re-routed. -
by_regionofGET /admin/modelsnow names the writer of.envtoo.
Each entry has a new fieldsource:envfor a writer that comes from
.envor from the Google key pasted in Settings, andscreenfor a
provider chosen on the Models page. Up to 1.0.8 a region written by such
a writer wasnullthere, and Settings said "No provider: their memories
wait" for the projects in it while they were written. For example, with
Scaleway chosen on the Models page andGOOGLE_API_KEYin.env:"by_region": { "eu": {"provider": "scaleway", "label": "Scaleway", "model": "gpt-oss-120b", "source": "screen"}, "global": {"provider": "google", "label": "Gemini", "model": "gemini-3.1-flash-lite", "source": "env"} }
A writer of
.envat an address no known provider uses belongs to no
region, and no entry names it. -
A writer saved after a refusal is tried at the drain's next pass.
After a provider refuses, the drain rests that region (one minute, then
longer, up to half an hour). Up to 1.0.8 a writer saved in place of the
refused one (a new key, another model or provider) waited out the old
one's rest, and/healthwent on listing the region underwritersas
refused. The rest now belongs to the writer that refused: saving another
one, or forgetting it, ends the rest and the line on/health. Saving the
same writer again keeps it. -
Two saves of the model settings at the same moment no longer fail.
Saving two providers at once, or saving while forgetting, could answer
500 (a duplicate or stale settings row) or keep only one of the two
choices. Each change of the provider key and of the writers now waits for
the one before it to commit. -
A fact a past statement closes now says which fact replaced it
(memory_core 6.3.0). "Northwind costs 55 now, it used to cost 50": the 50 is
closed where it was said, and before it had noinvalidated_by, though the
judge's closings always had one. Nowinvalidated_byon the 50 is the 55,
GET /v1/factsshows it, and when the 50 was an open fact before, the
fact.invalidatedwebhook carries it asinvalidated_bywhere it was null
(a fact born closed was never announced, and still is not). It is set only when the same write holds
exactly one open fact about the same subject and relation with another value;
with none or with several it stays empty. Nothing else closes and nothing is
judged. Facts closed this way before this release keep an empty pointer.
Forgetting the memory that said the past statement now gives the old value
back, as it already did for a closing by the judge: "Northwind is on the
Basic plan", then "moved to the Pro plan, it used to be on the Basic plan";
forget the second, and the first still says Basic, so Basic is true again,
as a new open fact. Before this release nothing was left open in that case.
The fact given back is of tensecurrent(a fact closed by a past statement
carriespast, and the given-back copy kept it while open; that was
corrected in the same change). -
A memory whose every declared fact the quality filter dropped now says
so in the log (memory_core 6.2.1). The filter drops a fact whose name is
not in the memory's text. Before, that was a DEBUG line per fact, so such a
memory ended done with no fact and nothing above DEBUG to tell it from a
text that holds none. Now one INFO line gives the count, never a word of the
text or of the fact. Which facts are kept is unchanged, and so is what the
store answers; memories already written with no fact are not re-read (the
commandmanage reextract --emptyfinds them). Limit: the memory is still
filed done, and a sentence with no subject ("She now lives in Lisbon") whose
name is in an earlier memory is still dropped; measuring that needs the
model and is not done. -
PATCH /v1/memories/{id}with the text the memory already has no longer
reads it again. Before, it queued the extraction once more, and if the
model answered no fact that second time, every fact the memory stated was
closed and the memory was marked done, though nothing had changed:
"Anna works in Milan" and "Anna has two children" were gone after a PATCH
that repeated the text. Now the reply is the memory as it is, with
facts: []and no model is called, so no cost either. A text that changed
is read again as ever and retires the facts it no longer states: editing a
value out of a memory still removes it. Limit: a text that changed by a
comma and whose second read answered no fact still closes the facts, because
the store cannot tell it from an edit that took the value away. Measured on
the tests of this change, not on LoCoMo or LongMemEval.
Security
- No change in what the installer trusts: it downloads only the release
files of the repository it names (nowkorely-ai/korely), over HTTPS,
checks them against the sha256 it carries, and stops when the answer comes
from another address. - The changes to the writers and to the model settings change nothing about
who can read or change them. - A PATCH with the same text changes nothing about who can read or write
what; its one log line carries the memory id and no text. - The INFO line of memory_core 6.2.1 carries a count and never a word of the
text or of a fact. - The pointer of memory_core 6.3.0 is an id of a fact of the same memory, same
account and same project: nothing crosses a tenant.