Releases: IkarusMK/AIcortex
Release list
v1.12.2 — Hotfix: don't forward 'resource' to the upstream IdP
Fixed — connector login, properly this time
v1.12.1's pin was not enough. Deeper finding: fastmcp 3.4.3 already contains the new authorize flow (CIMD, /consent interstitial, RFC 8707 resource forwarding). v1.11.0 only looked fine because existing connector sessions renew via refresh tokens — the last fresh login predated the 3.4.x flow, so the regression stayed invisible until a reconnect was needed.
The real, version-independent fix is in AICortex's own proxy construction:
forward_resource=False— the upstream IdP never sees theresourceparameter it rejects (invalid_request — "The 'resource' or 'scope' parameter is invalid", e.g. Pocket ID)require_authorization_consent="external"— no/consentinterstitial; the IdP's own (passkey) login is the consent, restoring the familiar flow
Both are signature-checked, so a fastmcp version without these kwargs can never break boot. New guard tests (tests/test_oauth_upstream.py) pin the contract against the installed fastmcp — the full suite passes against 3.4.4 (which Dependabot bumped to in #32, now safe thanks to the config fix).
Deploy: git pull && docker compose pull && docker compose up -d, then reconnect the connector in the Claude app.
v1.12.1 — Hotfix: pin fastmcp 3.4.3 (connector login)
Fixed
Claude custom-connector login broke after the v1.12.0 image rebuild.
The image silently picked up fastmcp 3.4.4 (loose >=3.4.2,<4 range). 3.4.4 forwards the RFC 8707 resource parameter that MCP clients send with /authorize on to the upstream IdP — IdPs without Resource-Indicator support (e.g. Pocket ID) reject the request:
invalid_request — The 'resource' or 'scope' parameter is invalid.
Root cause proven end-to-end: container logs show the IdP callback error, and the same Pocket ID authorize URL succeeds without resource and fails with it. fastmcp 3.4.3 dropped the parameter upstream, which is why v1.11.0 logged in fine — deployments were never at fault (callback URIs, env, client registration all verified good).
Fix: fastmcp==3.4.3 exact pin. The auth-critical dependency is now only bumped via a tested Dependabot PR — an image rebuild can no longer silently change the auth stack.
Deploy: git pull && docker compose pull && docker compose up -d, then reconnect the connector in the Claude app.
v1.12.0 — Admin WebUI + curl-based Bambu FTPS upload
Added — Admin WebUI at /ui
Manage the brain from a browser — no terminal needed. Served by the same container alongside /mcp (no extra service, no extra port), in the AICortex banner look, language switchable DE/EN, fully self-contained (strict CSP, no CDNs, no build step).
| Page | What you can do |
|---|---|
| Overview | Version, enforce status, live counts |
| Vault | Secret names only — add tokens/API keys/passwords (shared or per-user), delete. Values are write-only by design |
| Skills | Browse by category, view, create/edit/delete (same house rules as skill_write) |
| Services & devices | Read-only inventory of every registry incl. target + referenced secret name — only constructed fields leave the endpoint, nothing can leak |
| Users | Roles (admin/user/viewer) + per-user areas in policy.json |
| Logs | Authz audit log — who called which tool, allowed/denied and why; filterable, newest first |
Login: OIDC authorization code + PKCE against your own IdP (e.g. Pocket ID). Register ONE extra redirect URI: <BASE_URL>/ui/callback, then open <BASE_URL>/ui. Admin role required for management; signed HttpOnly session cookies; CSRF-guarded mutations; every action audited (names only, never values). Without OIDC (local testing) the UI is open on the localhost-only bind. Opt-out: UI_ENABLED=0.
The endpoints call the same module functions as the MCP tools (secrets_store.vault_*, skills helpers, tenancy._write_policy) — UI and assistant cannot drift.
Fixed — Bambu FTPS upload now goes through curl
The v1.10.2 session-reuse shim was not enough: Python's ftplib still failed to resume the TLS session on the DATA channel against the P1S SD store, so STOR hung into "read operation timed out". FTPS uploads (implicit and explicit) now shell out to curl (config via stdin — the password never touches argv), which implements FTPS session reuse natively. The vetted egress IP is pinned into curl (resolve=) so the SSRF/anti-rebinding guarantee survives the external process; TLS knobs (ca_bundle > tls_insecure > verify) match netguard.ssl_context. Plain FTP keeps the ftplib path. curl added to the image.
Tests: tests/test_webui.py (17) · tests/test_ftp_curl.py (7) — full suite 43 passed.
Deploy: git pull && docker compose pull && docker compose up -d — then add the /ui/callback redirect URI in your IdP.
v1.11.0 — Unified TLS everywhere (one helper, two knobs)
Changed
Unified TLS across ALL integrations. A central netguard.ssl_context(cfg) builds the SSL context for the socket clients (FTP, MQTT, IMAP, SMTP) from the same two knobs the HTTP clients already use via netguard.tls_verify: ca_bundle (pin a certificate — the safe way) and tls_insecure (verification off). Secure by default everywhere; only the admin *_add tools set it.
Added
ca_bundle / tls_insecure now at EVERY TLS spot. Previously only service_add/scan_add/webdav_add/caldav_add had the full pair. ftp_add/mqtt_add gain ca_bundle; imap_add/mail_add gain both — they had no TLS opt-out at all (a self-signed LAN mail server was unreachable). One consistent convention across services, scan, webdav, caldav, ftp, mqtt, imap, smtp — no more device-by-device patching. Includes the v1.10.2 FTPS session-reuse fix (Bambu upload).
v1.10.2 — FTPS-Upload zur Bambu (TLS-Session-Reuse)
Fixed
FTPS-Upload zur Bambu Lab (und anderen require_ssl_reuse-Servern) hängt nicht mehr → "read operation timed out". ftp_upload über implizites FTPS öffnete den Datenkanal mit einer FRISCHEN TLS-Session; Server, die verlangen, dass der Datenkanal die TLS-Session der Kontrollverbindung wiederverwendet (vsftpd require_ssl_reuse, Bambu-P1S-SD-Speicher), lassen solche Transfers hängen. _ImplicitFTP_TLS überschreibt jetzt ntransfercmd und wrappt den Datensocket mit session=self.sock.session. Damit läuft das Hochladen der geslicten .gcode.3mf auf die Drucker-SD (für den project_file-Start).
v1.10.1 — Scan-Fix: Scanner haengt nicht mehr (Drain bis 404)
Fixed
scan_document ließ den Scanner "busy" → der nächste Scan kam als HTTP 503.
eSCL verlangt, NextDocument so lange zu holen, bis 404 kommt — das gibt jede Seite zurück UND signalisiert dem Gerät, dass der Job fertig ist (Gerät wird frei). Der Code brach nach der ersten Seite ab, der Scanner blieb belegt, man musste am Display abbrechen und der Folgescan schlug mit 503 fehl. Jetzt wird bis 404 durchgezogen (inkl. 503-Warmup), und ADF-Mehrseitigkeit stimmt (Seite 1 behält den Namen, weitere -N; je Seite ein Paperless-Upload). Tests: tests/test_scan_drain.py.
Audited — kein Change nötig
IPP-Drucken wurde auf denselben Fehlertyp geprüft (auf Wunsch mit dem Scan-Fix). print_document schickt einen atomaren IPP Print-Job — alle Attribute + end-of-attributes + Dokumentbytes in einem POST mit Content-Length. Es gibt keinen "poll bis fertig"-Schritt zum Vergessen, der Drucker bleibt nicht belegt. Die übrigen Geräte-Tools (mqtt/ftp/webdav/ssh) sind fire-and-forget oder atomar — nur eSCL-Scannen hatte das Muster.
Full test suite 22/22 grün.
v1.10.0 — fs_view: der Assistent kann Workspace-Dateien SEHEN
Added
fs_view — Bild/PDF im Workspace mit Vision ansehen, nicht nur OCR.
Ein neues Tool rendert jede Bild- oder PDF-Datei unter /data/work zu Bild-Content, den das Modell direkt liest: Scans (scan_document), E-Mail-Anhänge (imap_fetch save_attachments), webdav/ftp-Downloads, Druckvorlagen. Schließt eine echte Lücke — bisher kam ein Scan nur als Pfad oder (über Paperless) als vermatschter OCR-Text zurück.
fs_view(path[, page, max_pages]) skaliert Rasterbilder herunter (Pillow, EXIF-aware) und rendert PDF-Seiten (pypdfium2). Gleiche harte Sandbox wie die anderen fs_*, Size-Cap (FS_VIEW_CEILING_BYTES, Default 30 MB), graceful degradation statt Crash.
Changed
- Neue Deps:
pillow>=11,<13,pypdfium2>=4,<6— beide mit manylinux cp314-Wheels, viauv pip compile --only-binary :all:gegen die Runtime-Python geprüft (pypdfium2 = Apache/BSD, bundelt PDFium, keine System-Libs wie poppler). - Docstrings + Katalog:
scan_documentundimap_fetchverweisen auffs_view.
Tests
tests/test_imaging.py+tests/test_fs_view.py— 13 Tests, alle grün.
v1.9.5 — per-service TLS options for call_service
Added
- Per-service TLS options —
service_addnow acceptstls_insecureandca_bundle, andcall_servicehonours them via the existingnetguard.tls_verify()resolver (secure by default, #10 pattern — same asscan_add/webdav_add). This makes self-signed LAN services (e.g. a Crafty panel, which failed withSSL: CERTIFICATE_VERIFY_FAILED) reachable without weakening the default: verification stays ON unless an admin explicitly opts out,ca_bundle(pinned cert) takes precedence overtls_insecure, andservice_listvisibly flags such services with[TLS-INSECURE]. Merge-safe: updating other fields never resets a configured TLS opt-out. New tests:tests/test_service_tls.py.
Changed
netguard.tls_verifydocstring now listsservice_addamong the admin-only writers of TLS opt-out configs.
Deploy: docker compose pull aicortex && docker compose up -d aicortex — then ping = v1.9.5. A self-signed service is then reachable via service_add(name, base_url, tls_insecure=true) (admin-only).
v1.9.4 — fix 421 host-guard rejection behind reverse proxy
Fixed
- auth/transport: the connector no longer returns 421 Misdirected Request on the OAuth discovery/registration routes behind a reverse proxy. fastmcp 3.4.3 enforces the MCP HTTP transport's Host/Origin DNS-rebinding guard, which rejected every request whose
Hostwas the public domain — so Claude's client registration failed ("Registrierung beim Anmeldedienst fehlgeschlagen"). The server now derives the Host/Origin allow-list fromBASE_URL(already required for OIDC, so it can't drift) and passes it tomcp.run(...);localhost/127.0.0.1stay allowed by the guard's defaults, andMCP_ALLOWED_HOSTS(comma-separated) can add more. Protection stays on — unknown hosts are still rejected. Regression introduced by the 3.4.3 bump in 1.9.3.
Deploy: docker compose pull && docker compose up -d (unpin the image back to :latest if you pinned :v1.9.2 during the incident). Verify: connector reconnects and the logs show no more 421 on the OAuth routes.
v1.9.3 — python 3.14 + dependency refresh + test CI
Changed
- Runtime bumped to Python 3.14 (
python:3.14-slim) and dependencies refreshed to current majors: paramiko 5, py-key-value-aio 0.4.5, fastmcp 3.4.3. Each validated before merge — the exact APIs used byserver.py/ssh_tools.pystill resolve, alllinux/cp314wheels exist (incl.uvloop), and the test scripts pass on 3.12 and 3.14. No client-facing behaviour/API change; a maintenance & hardening refresh. - CI actions pinned to current majors (checkout, buildx, login, metadata, build-push).
Added
- Real test signal on every PR and push (
tests.yml): install pinned deps on the runtime Python + byte-compile + run thetests/scripts. Previously PRs had no automated check (build.ymlonly builds/pushes the image). - Dependabot patch auto-merge (
dependabot-auto-merge.yml): patch-level PRs self-merge after an in-job gate (deps resolve + compile + test scripts + advisorypip-audit); minor/major stay open for manual review. Self-contained — no branch protection needed.
Fixed
- Test modules no longer hardcode an absolute local path on
sys.path;app/is resolved relative to each test file, so the suite runs in any checkout and in CI.