Repository navigation
Release Notes v1.4.10
Released: 2026-10-12
This release makes Sibyl safe to run as more than one replica. MCP now serves stateless streamable HTTP, OAuth and device-code flows keep their state in SurrealDB instead of process memory, settings changes propagate across replicas over a Redis invalidation bus, and validation receipts plus backup archives can live in S3 instead of a single-writer volume. Two performance changes move graph-build CPU work off the event loop and tune GC for long-running API processes.
🌟 Highlights
✨ Stateless MCP over HTTP
mcp_http_app() in apps/api/src/sibyl/main.py now serves the MCP streamable HTTP transport with stateless_http=True, dropping per-process session state entirely. /mcp accepts POST for RPC and lets OPTIONS through for CORS preflight; GET, DELETE and other methods get a 405 with an Allow: POST header instead of parking an idle stream. Any replica can answer any request, so MCP clients no longer need sticky routing. Write tools (add, remember, reflect, manage, and synthesis draft saves) are wrapped with the new @uninterruptible decorator in apps/api/src/sibyl/mcp_tools/completion.py so a client disconnect mid-write does not leave a half-applied change.
✨ Replica-safe OAuth, revocation and settings
MCP OAuth authorization state moved out of per-process dictionaries into the oauth_authorization_requests table (apps/api/src/sibyl/persistence/surreal/auth_runtime/oauth_authorization.py), transitioning pending → authenticated → code_issued through conditional updates. Request keys and codes are stored as SHA-256 hashes only. Device-code exchange, approval and denial use the same conditional-update pattern in device_authorization.py, closing the window where two replicas could redeem one code. Session revocations and settings changes broadcast over a new Redis invalidation bus (apps/api/src/sibyl/cache_invalidation.py, apps/api/src/sibyl/coordination/invalidation.py), with readiness reporting via apps/api/src/sibyl/api/readiness.py.
✨ Receipts and backup archives in S3
New backends in sibyl-core (backends/s3_client.py, backends/archive_store.py, backends/s3_archive_store.py, backends/s3_receipt_store.py) let validation receipts and organization backup archives live in object storage instead of a ReadWriteMany volume. Writes are create-only (If-None-Match), checksummed with sha256, and multipart for large archives; reads use range requests. apps/api/src/sibyl/backup_archives.py picks the S3 or directory store from configuration. Backup cleanup is now org-scoped, so one organization's cleanup job can no longer see or remove another's archives.
⚡️ Graph builds off the event loop
CPU-bound pure Python accounted for about 87% of cold graph-page build time. The new sibyl_core/services/graph_compute.py moves proof, verdicts, enumeration, narrowing, rendering and community detection onto a two-worker build pool, with a separate two-worker request pool for search, traverse and retrieval steps (steps under 64 rows stay inline). The worst event-loop stall on a 6,000-entity graph fell from about 1.1 s to about 0.44 s, and the search step's p99 wait to start during two concurrent builds fell from 0.9–1.4 s to about 55 ms. Output is byte-identical to the previous implementation, and rendering is now deterministic across processes (sorted walks, id tie-breaks), so replicas show the same clusters for the same graph.
⚡️ GC tuning for long-running API processes
apps/api/src/sibyl/gc_tuning.py freezes startup objects (about 522K on a real server) and raises GC thresholds to (10_000, 10, 100). Full collections per cold 6,000-entity graph build dropped from 6.7 (about 3.1 s total, with individual pauses of 137–650 ms) to 0.14 (about 60 ms total), and wall time for that build went from 18–19 s to 15–16 s. RSS stayed within about 15 MB across an 8-minute mixed-load run.
🤖 Multi-Replica Coordination
- Rate-limit storage now defaults to Redis database 4 with hard 0.5 s socket connect/operation bounds and in-memory fallback (
apps/api/src/sibyl/api/rate_limit.py), so a Redis blip degrades to per-replica limits instead of hanging requests. - The settings encryption key is generated once at startup and shared across replicas, verified by HMAC fingerprint (
apps/api/src/sibyl/services/settings_key.py,persistence/surreal/system_settings.py). - Startup recovery and manual source sync consult the job broker before resetting a crawl source (
coordination/broker.py:live_crawl_job_id(),api/routes/admin.py,api/routes/crawler.py). A sync against a live crawl returns409and recovery reports astill_runningcount instead of stomping an in-flight job. - The Redis coordination readiness check now verifies the Valkey connection, the arq queue and worker heartbeat (
coordination/_redis/broker.py). sibyld workergained--require-redisand--check. With--require-redisthe worker refuses to start on the local coordination backend;--checkis wired as the compose healthcheck.- Session revocation re-announcements are debounced to at most one per 30 s window using a new
user_sessions.revocation_announced_atfield and a conditional claim (auth_runtime/_common.py).
🐛 Fixes
- SurrealDB 3.2.x compatibility: queries now project every
ORDER BYfield in theirSELECTlist, and compound-indexINcleanups are pinned to avoid SurrealDB issue #7534 silently dropping mutations (auth_runtime/projects.py,graph_community_clusters.py,tools/admin.py). Covered by the newapps/api/tests/test_surreal_live_query_shapes.py. - Locked-setting error detail survives the error handler:
locked_by_env()inapps/api/src/sibyl/api/errors.pyis now shared by the settings and LLM routes and carriesfieldsanddeployment_ownedthrough to the client, so the admin UI can mark env-owned settings read-only with the specific field names (apps/web/src/lib/api/errors.ts,settings/admin/ai/page.tsx). - Rate-limit Redis URL from numeric ports: the Helm configmap applies
toStringto numeric port and db values before rendering the URL, which previously produced an unusable connection string. - SurrealDB export coverage: exports now discover namespaces and databases at runtime instead of a static list, write a per-run directory with a
manifest.json(sibyl-surrealdb-export, version 2), validate row counts before and after, and clean up stale incomplete runs older than a day. - Transient export failures retry: export and restore-drill jobs classify deterministic versus transient failures and gained
backoffLimitandactiveDeadlineSeconds, so a flaky HTTP call no longer burns the nightly run. - Version stamps keep Markdown tables aligned:
tools/release/sync_versions.pyre-pads table cells when a version changes length (1.4.9 → 1.4.10), which previously brokedocs:lintat release time. - HTTP error responses pass
exc.headersthrough (apps/api/src/sibyl/api/app.py), so headers such asRetry-AfterandAllowreach the client.
🔧 Backup and Restore Operations
- The restore drill decrypts per-file, verifies the manifest sha256, rejects exports older than
restoreDrill.maxExportAgeHours(default 36), and validates per-table row counts againstrestoreDrill.rowDrift.percent/.rowstolerances before reporting per-database results. - The scratch SurrealDB instance runs as a native sidecar, and
podFailurePolicyignores pods killed byDisruptionTargetso node drains do not count as drill failures. - Encryption hooks read
$SIBYL_EXPORT_FILEand write$SIBYL_EXPORT_ENCRYPTED_FILErather than encrypting in place. - New
jobDefaults.http.connectTimeoutSecondsandjobDefaults.http.maxTimeSecondsbound every curl call in the export and drill jobs.
🔨 Deployment and Packaging
- Ansible collection:
infra/ansibleis now packaged as thehyperb1iss.sibylcollection (galaxy.yml,meta/runtime.yml,infra/ansible/README.md), installable from git with a release-tag pin so the role and image versions stay in sync. - Frontend service account: the chart creates a dedicated
frontend.serviceAccountwithautomountServiceAccountToken: falseby default, so frontend pods carry no cloud role and cannot reach the receipt or archive stores. - Chart value validation: the configmap template rejects S3 URLs that are malformed, receipts and archives sharing a prefix, and both
urlandexistingClaimbeing set for the same store. - Compose:
docker-compose.prod.ymldefaultsSIBYL_COORDINATION_BACKENDtolocalwith an opt-in--profile redis, adds explicitSIBYL_REDIS_HOST/SIBYL_REDIS_PORT, generates a sharedSIBYL_SETTINGS_KEYin thereceipts-initstep, runs the worker with--require-redis, and adds asibyld worker --checkhealthcheck. - CI: a new
.github/actions/start-valkeyaction mirrors the Valkey image to dodge Docker Hub token failures and waits for readiness, letting the suite exercise the Redis coordination backend.
⚠️ Breaking Changes
- MCP
/mcpno longer acceptsGETorDELETE. Clients that opened a session stream withGETor revoked one withDELETEreceive405 Method Not AllowedwithAllow: POST.POSTandOPTIONSare unaffected. Update any client pinned to the session-based transport. backend.service.sessionAffinityandbackend.service.sessionAffinityConfigare removed from thesibylchart and its service template. Remove them from your values file; stateless MCP no longer needs sticky routing.backend.validationReceipts.existingClaimmoved under thevalidationReceiptsblock, which now also acceptsurlfor S3. Exactly one ofurlorexistingClaimmust be set.- The
sibyl-surrealdbchart requires Kubernetes 1.29+.charts/surrealdb/Chart.yamlnow declareskubeVersion: ">=1.29.0-0"for native sidecars andpodFailurePolicy. The mainsibylchart declares nokubeVersionconstraint and is unaffected. - The worker refuses the local coordination backend when started with
--require-redis, which is now the default indocker-compose.prod.yml. - Settings owned by the deployment environment return
409withdetail.error = "locked_by_env"plusfieldsanddeployment_ownedlists instead of being silently accepted. Clients parsing the old error shape need updating. - In-flight OAuth and device-code flows do not survive the upgrade, since flow state moved from process memory to SurrealDB. Users mid-authorization need to restart the flow.
- SurrealDB exports changed format. Old flat export files are not readable by the new per-run manifest layout. Retention and sync scripts that assume flat files need to handle run directories.
📝 Upgrade Notes
- Upgrade the
sibylandsibyl-surrealdbcharts to1.4.10. Confirm your cluster is on Kubernetes 1.29 or newer before upgradingsibyl-surrealdb. - Remove
backend.service.sessionAffinityandbackend.service.sessionAffinityConfigfrom your values file, and moveexistingClaimunderbackend.validationReceipts. - To move receipts and archives to S3, set
backend.validationReceipts.urlandbackend.backupArchives.url(orSIBYL_VALIDATION_RECEIPT_URL/SIBYL_BACKUP_ARCHIVE_URL) usings3://bucket[/prefix][?region=name]. Receipts and archives must not share a prefix. GrantPutObject(withIf-None-Matchand multipart),GetObject(range),DeleteObject,ListObjectsV2andAbortMultipartUpload. Installsibyl-core[s3]for theboto3/botocoredependency. SIBYL_BACKUP_DIRnow defaults to~/.sibyl/backupsinstead of./backups. Set it explicitly if you relied on the old relative path.SIBYL_BACKUP_FREE_SPACE_RESERVE_MB(default 512) reserves headroom for the directory store.- For multi-replica deployments, point every replica and worker at the same Redis/Valkey instance and distribute one
SIBYL_SETTINGS_KEY. Replicas with mismatched keys fail the HMAC fingerprint check at startup. - Drop any static
databaseslist from yoursibyl-surrealdbvalues; namespaces and databases are discovered at runtime. Review your encryption hook to confirm it reads$SIBYL_EXPORT_FILEand writes$SIBYL_EXPORT_ENCRYPTED_FILE, and setrestoreDrill.maxExportAgeHoursplusrestoreDrill.rowDrifttolerances for your data volume. - Ansible users should install the collection instead of checking out the role directory:
then run
# requirements.yml collections: - name: https://github.com/hyperb1iss/sibyl.git#/infra/ansible type: git version: v1.4.10
ansible-galaxy collection install -r requirements.ymland applyhyperb1iss.sibyl.sibyl. - Expect a latency trade-off during graph builds: with CPU work on worker pools, other requests see a higher median from GIL contention but a lower tail. Remaining event-loop stalls are the SurrealDB SDK's frame decoding and the
/graph/clustersLouvain run, both tracked as follow-ups.
Install
Local server
curl -fsSL https://raw.githubusercontent.com/hyperb1iss/sibyl/main/install.sh | sh -s -- --version 1.4.10Remote CLI
curl -fsSL https://raw.githubusercontent.com/hyperb1iss/sibyl/main/install.sh | sh -s -- --remote --version 1.4.10
sibyl init --remote https://sibyl.example.com
sibyl auth loginHomebrew
brew install hyperb1iss/tap/sibyl
sibyl upArch Linux (AUR)
paru -S sibyl
sibyl upHeadless server
curl -fsSL https://raw.githubusercontent.com/hyperb1iss/sibyl/main/install.sh | sh -s -- --version 1.4.10 --no-openKubernetes (Helm)
helm repo add sibyl https://raw.githubusercontent.com/hyperb1iss/sibyl/gh-pages
helm repo update sibyl
helm upgrade --install sibyl sibyl/sibyl --version 1.4.10Artifacts
This release includes Python wheels and sdists, the generated
Homebrew formula, the generated AUR PKGBUILD, Helm charts, Docker
SBOMs, aggregate dual-registry cosign receipts, and a SHA256 checksum
manifest.