Repository navigation
Releases: fireflyframework/flycanon
Release list
v26.10.4
Workspace creation now returns a clear 400 workspace_scope_mismatch when the body id differs from X-Workspace-Id. The request is rejected before a row or lifecycle event is written. Matching requests retain 201 and their existing response.
The API reference now describes the actual workspace lookup error and list behavior. Python and Java SDK instructions explain how to select the workspace being created. Server and SDK versions are aligned at 26.10.4; dependency pins, RLS policies, SDK methods and request/response schemas are unchanged. The published Python SDK 26.10.3 remains compatible with the corrected server.
Local qualification covers the production non-bypass PostgreSQL role, actual HTTP and SDK calls, persisted rows and outbox events, foreign-scope visibility and rejected credentials. The configured unit suite passes 1,219 tests, with 52 integration tests passing and three optional backend integrations skipped. Python and Java SDK suites pass 61 and 62 tests.
Lifecycle-event delivery remains best-effort after a successful workspace write. Platform API keys retain their existing trusted operator authority.
Install the Python SDK from the release wheel:
uv add https://github.com/fireflyframework/flycanon/releases/download/v26.10.4/flycanon_sdk-26.10.4-py3-none-any.whlFor the standalone server, follow the released-server installation guide. Verify downloaded files against SHA256SUMS. Java consumers use com.firefly:flycanon-sdk:26.10.4 through GitHub Packages.
v26.10.3
Python SDK
Install the wheel for this release with uv:
uv add https://github.com/fireflyframework/flycanon/releases/download/v26.10.3/flycanon_sdk-26.10.3-py3-none-any.whlOr pin it in your pyproject.toml:
[project]
dependencies = ["flycanon-sdk"]
[tool.uv.sources]
flycanon-sdk = { url = "https://github.com/fireflyframework/flycanon/releases/download/v26.10.3/flycanon_sdk-26.10.3-py3-none-any.whl" }Java SDK
<dependency>
<groupId>com.firefly</groupId>
<artifactId>flycanon-sdk</artifactId>
<version>26.10.3</version>
</dependency>Resolve from GitHub Packages -- add to your pom.xml:
<repositories>
<repository>
<id>github-fireflyframework</id>
<url>https://maven.pkg.github.com/fireflyframework/flycanon</url>
</repository>
</repositories>What's Changed
Full Changelog: v26.10.2...v26.10.3
Canon 26.10.2 — stable Agentic adoption
Stable Agentic adoption
Canon 26.10.2 adopts the published Agentic 26.10.1 wheel and aligns the server, Python SDK, and Java SDK versions. The HTTP schemas change only their version metadata. Canon's application code, migrations, Azure support, and existing RLM behavior remain unchanged.
pyfly stays at the qualified v26.09.04 reference. A later pyfly upgrade needs a separately qualified drain or replay of queued legacy EDA events; this release does not perform that cutover.
Install
For the server, follow Install the released server. The release requirements file selects the server wheel, qualified pyfly reference, and immutable public Agentic wheel. Use the matching source archive for the migration scaffold and runtime configuration. SHA256SUMS covers all eight package and operator assets.
Install the Python SDK directly from this release:
uv add https://github.com/fireflyframework/flycanon/releases/download/v26.10.2/flycanon_sdk-26.10.2-py3-none-any.whlThe Java SDK is available as com.firefly:flycanon-sdk:26.10.2 through GitHub Packages, with a downloadable JAR also attached here.
Verification
All seven required checks passed on the reviewed PR commit before the guarded merge. Local native validation passed 1,270 unit and integration tests; three optional Chroma/Qdrant cases were skipped because their clients were absent. Fresh installations from the public server requirements and Python SDK wheel passed, including all 59 SDK tests on Python 3.11.
The full installed application also passed authenticated ingestion, cited hybrid retrieval, tenant/workspace isolation, scoped agent-token refusals, and native PostgreSQL row-security checks in an owned local environment. That environment used an explicitly synthetic, deterministic loopback embedding service; it did not execute an external model. No paid provider calls or cloud application deployment were used. The container publisher verifies image publication separately from application runtime acceptance.
Earlier release tags and assets remain unchanged.
flycanon 26.10.1
Packages and installation
This patch aligns the flycanon server and its Python and Java clients at 26.10.1. The server installation guide selects the matching wheel, runtime requirements and configuration assets from the canonical GitHub release. SDK guides use the same version and the current Firefly framework-family banners across desktop/mobile and light/dark variants.
The Python SDK package metadata now identifies fireflyframework. Modified handwritten SDK sources retain their Foundation copyright and include the Apache 2.0 SPDX identifier. SDK version constants and user-agent headers match the package version.
Compatibility and verification
The HTTP contract, server implementation, client schemas, migrations, runtime configuration and framework dependency pins are unchanged. Both lockfiles change only the project's own version.
Local validation passed 1,213 server unit tests, 59 Python SDK tests, lint, formatting, type checks, Java 25 Maven verification and server/SDK package builds. Five existing local tests require Docker; the configured PR integration and container checks run separately before publication. The release includes checksums for the server/client packages and operator configuration files. Earlier releases remain available with their original assets.
Operators should apply the installation guide to their own configured PostgreSQL, object-store and authentication environment. No paid model-provider calls or cloud application deployments were used for this release validation.
Python SDK
Install the wheel for this release with uv:
uv add https://github.com/fireflyframework/flycanon/releases/download/v26.10.1/flycanon_sdk-26.10.1-py3-none-any.whlOr pin it in your pyproject.toml:
[project]
dependencies = ["flycanon-sdk"]
[tool.uv.sources]
flycanon-sdk = { url = "https://github.com/fireflyframework/flycanon/releases/download/v26.10.1/flycanon_sdk-26.10.1-py3-none-any.whl" }Java SDK
<dependency>
<groupId>com.firefly</groupId>
<artifactId>flycanon-sdk</artifactId>
<version>26.10.1</version>
</dependency>Resolve from GitHub Packages -- add to your pom.xml:
<repositories>
<repository>
<id>github-fireflyframework</id>
<url>https://maven.pkg.github.com/fireflyframework/flycanon</url>
</repository>
</repositories>What's Changed
- Document verified standalone server installation by @ancongui in #78
- Align knowledge source and identity with Firefly Framework by @ancongui in #79
- Use framework family banners throughout SDK guides by @ancongui in #80
- Release canonical server and SDK packages as 26.10.1 by @ancongui in #81
Full Changelog: v26.10.0...v26.10.1
Flycanon 26.10.0
Phone PII matching now respects hexadecimal identifier boundaries. Numeric fragments inside revision and digest identifiers no longer reject canonical memory writes. Standalone US and international phone numbers remain detected, and strict PII rejection remains enabled.
Server, Python SDK and Java SDK versions and runtime User-Agents are aligned at 26.10.0. The HTTP contract is unchanged. The release includes server and Python SDK wheels/source archives, the Java SDK JAR and SHA256SUMS.
Install the Python SDK directly from this release:
uv add https://github.com/firefly-operationOS/flycanon/releases/download/v26.10.0/flycanon_sdk-26.10.0-py3-none-any.whlServer wheel: flycanon-26.10.0-py3-none-any.whl (Python 3.13). Java: com.firefly:flycanon-sdk:26.10.0 from GitHub Packages. Container: ghcr.io/firefly-operationos/flycanon:26.10.0 for Linux amd64 and arm64.
Validation includes the server PII regression suite, full server/SDK tests, native Docker integration, lint/type checks and the Docker image build. Cloud authentication and external provider acceptance are separate deployment checks. BuildKit SBOM/provenance publication is disabled; the workflow's attestation upload is advisory.
Standalone server installation
Use the verified server installation guide and versioned runtime requirements. A wheel installer does not consume repository uv-source settings: the requirements select PyFly v26.09.04 and Agentic v26.06.16 explicitly. A fresh full runtime installation resolved all 218 packages and served version 26.10.0; authenticated source/memory retrieval passed for two independently scoped project workspaces.
Keep the released source archive's pyfly.yaml, alembic.ini, migration directory and environment template in the runtime working directory. The configuration supplement is byte-identical to the released configuration. Starting without it can activate unintended framework defaults, including Kafka and a separate management listener. Configure the PostgreSQL database, credentials and provider settings for your operator environment before starting.
These installation supplements are documented in PR 78. The existing tag, wheel, source archives and Java package are unchanged; SHA256SUMS now covers all eight payload files. GHCR image access requires the registry package's permissions.
v26.9.0
Python SDK
Install the wheel for this release with uv:
uv add https://github.com/firefly-operationOS/flycanon/releases/download/v26.9.0/flycanon_sdk-26.9.0-py3-none-any.whlOr pin it in your pyproject.toml:
[project]
dependencies = ["flycanon-sdk"]
[tool.uv.sources]
flycanon-sdk = { url = "https://github.com/firefly-operationOS/flycanon/releases/download/v26.9.0/flycanon_sdk-26.9.0-py3-none-any.whl" }Java SDK
<dependency>
<groupId>com.firefly</groupId>
<artifactId>flycanon-sdk</artifactId>
<version>26.9.0</version>
</dependency>Resolve from GitHub Packages -- add to your pom.xml:
<repositories>
<repository>
<id>github-firefly-operationOS</id>
<url>https://maven.pkg.github.com/firefly-operationOS/flycanon</url>
</repository>
</repositories>What's Changed
- The answer engine speaks Azure, and three places stop creating an extension a managed PostgreSQL reserves by @ancongui in #75
Full Changelog: v26.8.0...v26.9.0
v26.8.0
Python SDK
Install the wheel for this release with uv:
uv add https://github.com/firefly-operationOS/flycanon/releases/download/v26.8.0/flycanon_sdk-26.8.0-py3-none-any.whlOr pin it in your pyproject.toml:
[project]
dependencies = ["flycanon-sdk"]
[tool.uv.sources]
flycanon-sdk = { url = "https://github.com/firefly-operationOS/flycanon/releases/download/v26.8.0/flycanon_sdk-26.8.0-py3-none-any.whl" }Java SDK
<dependency>
<groupId>com.firefly</groupId>
<artifactId>flycanon-sdk</artifactId>
<version>26.8.0</version>
</dependency>Resolve from GitHub Packages -- add to your pom.xml:
<repositories>
<repository>
<id>github-firefly-operationOS</id>
<url>https://maven.pkg.github.com/firefly-operationOS/flycanon</url>
</repository>
</repositories>What's Changed
Full Changelog: v26.7.1...v26.8.0
v26.7.1 — Multi-tenant hardening for a shared deployment
Multi-tenant hardening for a shared deployment
This release is what a second caller found. The dworkers control plane runs one shared flycanon for all of its tenants, and wiring it against 26.7.0 turned up defects a single-tenant stack cannot see — where every process is the database owner and the only client is the one that wrote the docs. The API-key gate that was read by nothing is now enforced, an erased document is erased from the object store and not only from the index, outbound fetches and callbacks refuse anything that is not globally routable, callbacks are signed, a whole workspace can be purged, search returns rows under the NOBYPASSRLS serving role that docs/deployment.md prescribes, the boot-time DDL that role could not run has moved into a migration, and the RLM can finally be pointed at Claude 4.6+ / 5.
Security
FLYCANON_API_KEYSis now enforced. The setting existed since the first release and was read by nothing: anyone who could reach port 8500 could read or write any tenant and mint agent tokens for it.ApiKeyMiddlewarenow requiresX-API-Key: <key>orAuthorization: ApiKey <key>on every/api/v1/*route exceptGET /api/v1/version, and on the admin dashboard, whenever at least one key is configured (401 missing_api_key/401 invalid_api_key, RFC 7807, constant-time comparison)./api/v1/agent/*requests that carryX-Agent-Tokenare exempt -- the token is their credential. With the setting empty the service stays open as before and the boot log says so (api-key gate DISABLED); with keys it logsapi-key gate ENABLED: n key(s).- Admin dashboard requires authentication (
pyfly.admin.require-authdefaults totrue, overridable withFLYCANON_ADMIN_REQUIRE_AUTH=falsefor loopback dev). A validated platform key is bridged into pyfly'sSecurityContext(ApiKeyPrincipalFilter) so the dashboard answers to the key and refuses everything else. - Outbound host policy (SSRF guard).
urifetches andcallback_urlwebhooks refuse every address that is not globally routable -- loopback, private (RFC 1918 / ULA), link-local (instance metadata), multicast, reserved, unspecified, RFC 6598 shared address space (100.64.0.0/10: CGNAT, Tailscale, the pod CIDR of most managed Kubernetes clusters), site-local, documentation and benchmarking blocks; the decision isipaddress'sis_global, the named checks only shape the message -- literal, DNS-resolved, and on every redirect hop (redirects are now followed by hand, at most five) -- with400 url_fetch_forbidden_host/400 callback_url_not_allowed.FLYCANON_URL_FETCH_ALLOW_PRIVATE=truelifts the denylist for private dev stacks. Fetcher failures now render as problem+json (url_fetch_*codes, 400 / 413 / 502) instead of the framework 500. Documented gap: DNS rebinding between check and dial. - Signed webhooks. Async-ingest callbacks carry
X-Flycanon-Signature: t=<unix>,v1=<hex HMAC-SHA256 over the raw body>whenFLYCANON_WEBHOOK_SECRETis set (unsigned + boot warning otherwise), and the payload now carriestenant_id/workspace_id.verify_signatureships inflycanon.web.conventions.webhook_signature.
Added
- User-tier
DELETE /api/v1/sources/{id}(204;404 source_not_found). Both it and the agent-tier DELETE now delete the stored original from the object store as well as the index, chunks and row -- before, an "erased" document stayed readable by the RLM corpus.IntakeService.removereturns aSourceRemoval, and itsoriginal_deletedis a fact, not a promise: the store is asked whether the object exists before the (no-op-on-missing) delete, and a key that is not in this process's store is logged as a warning and reportedfalse-- the audit payload, theSourceRemovedevent and the purge counter all carry that same value. POST /api/v1/workspaces/{id}:purge->WorkspacePurgeResult. Removes every source through the full pipeline, then knowledge items / versions / citations / relations, candidates, conversations / turns, ingest jobs / events and cost events in one transaction, closes the workspace and emitsWorkspaceDeleted; audit rows are kept and aworkspace.purgedrow records the counts.X-Workspace-Idmust equal the path id (400 workspace_scope_mismatch). Idempotent in the literal sense: every counter is what this call erased,originals_deletedcounts objects actually found and deleted, andclosedistrueonly for the call that moved the row toclosed(WorkspaceRepository.close_if_open); a repeat is all zeros withclosed: false, restamps nothing, publishes no secondWorkspaceDeletedand writes no second audit row.- Job-stream resume.
GET /api/v1/ingest-jobs/{id}/streamaccepts?after_id=<id>and the standardLast-Event-IDheader, and stampsid:on everyeventframe, so a reconnect no longer replays the whole history (the docs had promisedafter_idsince 26.5). FLYCANON_ADMIN_DATABASE_URL.WorkspaceRepository.list_for_tenant(GET /api/v1/workspaces) runs on this BYPASSRLS engine; under the productionflycanon_approle the request engine could only ever return the caller's own header workspace.- OpenAPI wire contract.
openapi.jsondeclaressecuritySchemes(ApiKeyHeader,ApiKeyAuthorization,AgentToken) and reusable header parameters (X-Tenant-Id,X-Workspace-Id,X-Correlation-Id,Idempotency-Key) attached to every operation, so a generated client sends the mandatory headers.securitydescribes what satisfies the operation, not what the middleware lets through:/api/v1/agent/*listsAgentTokenalone (the platform key is neither required nor accepted there -- the route answers401 missing_agent_token), every other tenant operation lists the two platform-key forms. - Scope on ingest events. Every
flycanon.ingestpayload (SourceIngested,SourceReplaced,SourceRemoved,SourceIngestionFailed,IngestSourceRequested,IngestSourceFinished,IngestSourceFailed) carriestenant_id/workspace_id-- including theIngestSourceRequestedthe stuck-job sweep republishes:IngestJobRepository.reclaim_stuckreturnsReclaimedJob(job_id, tenant_id, workspace_id)and the service has no unscoped publish path.
Changed
- Conversations use the same engine as
/query.ConversationServiceis wired toAnswerDispatcher, soPOST /conversations/{id}/turnanswers with RLM by default and only with the deprecated RAG engine underFLYCANON_ANSWER_MODE=rag(then with theX-Flycanon-Deprecationheader, like/query). - Framework pins moved to pyfly
v26.09.04and fireflyframework-agenticv26.06.14; the unit suite, ruff and pyright pass unchanged on them.uv.lockis now committed and CI / the Dockerfile runuv sync --locked. - API and worker no longer share a Postgres-outbox consumer group. pyfly's CQRS auto-configuration subscribes a cache-invalidation bridge on
*in every process, so an API on the worker's group advanced the shared cursor pastIngestSourceRequestedevents and?mode=asyncjobs stayedqueuedforever. The API defaults toflycanon-api,flycanon workertoflycanon-workers(FLYCANON_EDA_GROUPoverrides either). FLYCANON_RLM_ANSWER_MODELremoved.CanonSettings.rlm_answer_modelshipped with the RLM settings in 26.7.0, documented as "the model for the final single-shot answer synthesis", and was read by nothing: the RLM's final answer is produced by the root model, either as thefinal(...)tool call of the CodeAct loop or as the tool-less forced-final turn when the loop runs out (bothchat_rawturns onFLYCANON_RLM_ROOT_MODEL); the sub model serves thellm()/rlm()helpers and the self-consistency selector. There is no single-shot synthesis step for a third model to drive, so the knob is gone rather than wired;CanonSettingsignores unknownFLYCANON_*variables, so an env file that still sets it boots unchanged. The docs,env_templateand the README now name two RLM models and say which one answers (tests/unit/rlm/test_which_model_answers.pyrecords the request bodies of a whole session and pins it).- Both entry points export pyfly's
_PYFLY_SERVER_*variables. pyfly logsserver_startedin every process with0.0.0.0:8080as the fallback.flycanon serveexportsuvicorn/0.0.0.0/FLYCANON_PORTso the line reports the real socket;flycanon worker, which runs no server, exportsnone/-/0so the line cannot claim a listener the process does not hold, and logs its own "no HTTP listener in this process" line beside it.
Fixed
- The shipped image could not ingest. The localfs object-store default
./var/objectsresolved to the root-owned/app/varunder thecanonuser, so the first ingest failed with EACCES. The image now exportsFLYCANON_OBJECT_STORE_LOCALFS_ROOT=/app/canon-data/objects(volume-backed) and pre-creates/app/canon-data/objectsand/app/var/objectswithcanonownership. - Sandbox child on macOS.
RLIMIT_ASis not settable on Darwin; the RLM sandbox runner now tolerates that refusal there (CPU and file-size caps still apply) instead of dying at startup, which had failed 27 unit tests on every developer Mac. - Zero search hits under the application role.
PostgresCorpus(the BM25 channel) ran its reads on a bare Core connection of its own engine, where the ORMafter_beginlistener that sets theapp.tenant_id/app.workspace_idGUCs never fires; under FORCE RLS aNOBYPASSRLSserving role (flycanon_app, asdocs/deployment.mdprescribes) therefore saw no chunk on the lexical channel and no row on the RRF hydration, and/searchand/agent/searchansweredhits: []while the vector channel alone found the chunk. Invisible in a stack where every process is the database owner. Every scoped corpus read now binds the GUCs itself, from the explicit(tenant_id, workspace_id)it is handed, through `set_conf...
v26.7.0 — Agent-tier source replace + delete
Agent-tier source lifecycle: replace and delete
This release completes the agent-tier source API so an automated caller can keep an indexed document in sync across its whole lifecycle — not just create it. A machine client holding an agent.sources:ingest token can now replace a source's content in place or remove it entirely, with the dense vectors and BM25 rows kept consistent on every path. It also makes pyproject.toml the single source of truth for the service version.
Added
- Agent-tier source replace.
PUT /api/v1/agent/sources/{source_id}(scopeagent.sources:ingest, mandatoryIdempotency-Key) re-ingests an existing source in place through the same pipeline as the user-tier PUT: chunks and dense vectors are replaced under the samesource_id.content_base64is required; replays dedup under a route-specific scope. - Agent-tier source delete.
DELETE /api/v1/agent/sources/{source_id}(scopeagent.sources:ingest, mandatoryIdempotency-Key,204) removes a source within the tenant/workspace scope: BM25 rows and dense vectors are purged, chunk rows and the source row are deleted, an audit entry is recorded and aSourceRemovedEDA event is published. Object-store originals are not touched. A retried DELETE with the same key replays the original204.
Changed
- Single-source version.
__version__(and the version reported bypyfly_application/GET /api/v1/version) is now derived from the installed package metadata viaimportlib.metadata, sopyproject.tomlis the only place a release bump touches.
Python SDK
Install the wheel for this release with uv:
uv add https://github.com/firefly-operationOS/flycanon/releases/download/v26.7.0/flycanon_sdk-26.7.0-py3-none-any.whlOr pin it in your pyproject.toml:
[project]
dependencies = ["flycanon-sdk"]
[tool.uv.sources]
flycanon-sdk = { url = "https://github.com/firefly-operationOS/flycanon/releases/download/v26.7.0/flycanon_sdk-26.7.0-py3-none-any.whl" }Java SDK
<dependency>
<groupId>com.firefly</groupId>
<artifactId>flycanon-sdk</artifactId>
<version>26.7.0</version>
</dependency>Resolve from GitHub Packages -- add to your pom.xml:
<repositories>
<repository>
<id>github-firefly-operationOS</id>
<url>https://maven.pkg.github.com/firefly-operationOS/flycanon</url>
</repository>
</repositories>What's Changed
- feat(agent): replace + delete endpoints for agent-tier sources by @miguelgfierro in #69
- docs: document agent-tier source replace + delete by @miguelgfierro in #70
- docs(changelog): add 26.6.19 entry by @miguelgfierro in #68
- chore: bump version to 26.7.0 by @miguelgfierro in #71
Full Changelog: v26.6.19...v26.7.0
v26.6.19
RLM reliability fixes + tuning levers
This release hardens the RLM answer engine so it can no longer return a silent empty answer, and adds two opt-in levers for hard questions.
Fixed
- Empty-answer bug. When the CodeAct loop exhausted its iteration budget without calling
final(), it returned an empty string flagged as a valid answer (no_answer=false) that callers could not detect. A blank result is now surfaced asno_answer=truewith an explanatory note, and the out-of-iterations fallback re-asks the model with no tools so a diverged run must emit text instead of nothing.
Added
- CodeAct turn logging + convergence warnings — per-turn DEBUG trace and a WARNING when the loop exhausts its budget without converging.
- Per-request
extended_reasoning(AnswerRequest.extended_reasoning, defaultfalse) — runs the engine at twice the configuredFLYCANON_RLM_MAX_ITERSfor a single hard request, without a global default bump. RAG ignores it. - Self-consistency guard (
FLYCANON_RLM_SELF_CONSISTENCY, default1= off) — when>1, the non-streaming answer path runs the engine N times in parallel and an LLM selector returns the most-grounded answer; token usage of all runs + the selector is merged.
Changed
- Scoped RLM system prompt — requires verifying every benchmark/threshold/figure against the source documents and forbids stating a value not found in them.
Build
boto3is now declared in thedevextra so the S3 object-store backend's unit tests and the pyright type-check ofstorage/s3.pyrun deterministically in CI (they previously relied on a transitive path that went away).
Python SDK
Install the wheel for this release with uv:
uv add https://github.com/firefly-operationOS/flycanon/releases/download/v26.6.19/flycanon_sdk-26.6.19-py3-none-any.whlOr pin it in your pyproject.toml:
[project]
dependencies = ["flycanon-sdk"]
[tool.uv.sources]
flycanon-sdk = { url = "https://github.com/firefly-operationOS/flycanon/releases/download/v26.6.19/flycanon_sdk-26.6.19-py3-none-any.whl" }Java SDK
<dependency>
<groupId>com.firefly</groupId>
<artifactId>flycanon-sdk</artifactId>
<version>26.6.19</version>
</dependency>Resolve from GitHub Packages -- add to your pom.xml:
<repositories>
<repository>
<id>github-firefly-operationOS</id>
<url>https://maven.pkg.github.com/firefly-operationOS/flycanon</url>
</repository>
</repositories>What's Changed
- chore: bump pyfly to v26.06.113 by @miguelgfierro in #66
- fix(rlm): eliminate empty-answer failure; add extended_reasoning + self-consistency by @miguelgfierro in #67
Full Changelog: v26.6.18...v26.6.19