Skip to content

Releases: mabaeyens/AnthropicExtension

v0.5.5

Choose a tag to compare

@mabaeyens mabaeyens released this 15 Sep 10:04

Fixed

  • Model registry updated to the current Claude generation: claude-sonnet-4-6 -> claude-sonnet-5, claude-opus-4-8 -> claude-opus-5 (js/config.js API.MODELS and API.CONTEXT_WINDOWS). Haiku 4.5 is unchanged, still current. The old IDs were stale and no longer resolve upstream.

Pairs with proxy proxy-v2.0.1 — deployments should update ALLOWED_ANTHROPIC_MODELS if it's set explicitly in the proxy's .env.

See CHANGELOG.md for full details.

proxy-v2.0.2 — TLS dev-certificate constraint fix

Choose a tag to compare

@mabaeyens mabaeyens released this 15 Sep 10:42

Security-relevant fix: TLS dev certificate was not properly constrained

Summary for security review: the local development TLS certificate generated by proxy/scripts/setup.ps1 -DevCert (and the manual openssl command previously documented in proxy/README.md) was a self-signed certificate with basicConstraints: CA:TRUE and no keyUsage/extendedKeyUsage restriction. In X.509 terms, the certificate the proxy presented for TLS was itself flagged as a valid Certification Authority rather than a constrained end-entity certificate scoped to serving TLS for one hostname.

  • Scope: local development certificate generation only. This is not the certificate used for QPS mutual-TLS authentication (QLIK_CERT/QLIK_KEY, unaffected), and it never leaves the local certs/ directory (git-ignored, never committed).
  • Why it wasn't caught sooner: Windows CryptoAPI and Chrome accept a CA-flagged certificate for TLS without complaint. Firefox's strict path-building validator (mozilla::pkix) correctly rejects it (MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY), which is how this was caught during a deployment.
  • Fix: certificate generation is now a proper two-tier setup:
    1. A dedicated local development CA (certs/ca/, generated once per checkout, basicConstraints=critical,CA:TRUE,pathlen:0) — unrelated to and independent of any CA used elsewhere in a customer's Qlik or corporate PKI.
    2. A constrained end-entity leaf certificate signed by that CA (basicConstraints=critical,CA:FALSE, keyUsage=critical,digitalSignature,keyEncipherment, extendedKeyUsage=serverAuth) — usable only to serve TLS for the proxy's configured hostname(s), not to sign anything else.
  • Trust model improvement: -TrustCert now imports only the CA into the OS trust store, never the leaf, so a future certificate rotation or an added hostname never requires re-importing anything into the trust store.
  • Key handling: both the CA's and the leaf's private keys are generated locally, never transmitted anywhere, and are git-ignored. The CA key is treated as more sensitive than the leaf's (it alone can mint further certificates) and is isolated in its own certs/ca/ directory.
  • Remediation for existing deployments: regenerate the certificate via scripts/setup.ps1 -DevCert -TrustCert. The previous non-conformant certificate never left the local filesystem, so this is a local remediation, not a supply-chain or distribution concern.

Also in this release

  • -CertHosts is now a comma-separated string parameter, not a PowerShell array (an array-typed parameter does not reliably survive a pwsh -File invocation from outside PowerShell).
  • Documented: npm ci deletes the node-windows service-wrapper dependency (deliberately excluded from package-lock.json), which crashes an already-installed Windows service with Error 1067 on its next restart. Fix: npm install --no-save node-windows, then Restart-Service <name>.

Pairs with extension v0.5.5.

See CHANGELOG.md for full details.

proxy-v2.0.1

Choose a tag to compare

@mabaeyens mabaeyens released this 15 Sep 10:05

Fixed

  • Default ALLOWED_ANTHROPIC_MODELS allowlist updated to the current Claude generation (lib/model-allowlist.js, .env.example): claude-haiku-4-5,claude-sonnet-4-6,claude-opus-4-8 -> claude-haiku-4-5,claude-sonnet-5,claude-opus-5, matching the extension's updated registry. A deployment with ALLOWED_ANTHROPIC_MODELS set explicitly in its own .env must update that value too, or it will 403 Model not allowed the new IDs.

Added

  • SERVICE_NAME env var for service/install-service.js / service/uninstall-service.js, so an operator can register the Windows service under a site-specific name instead of the hardcoded cm-llm-proxy. Default behavior is unchanged when unset.

Pairs with extension v0.5.5.

See CHANGELOG.md for full details.

v0.5.4: one object owns the configuration, self-clearing config banner

Choose a tag to compare

@mabaeyens mabaeyens released this 27 Jul 19:10

See CHANGELOG.md for the full entry. Fixes: a second extension object in the app no longer hijacks the shared configuration (it was overwriting the configured model with its untouched Haiku default, which then fell through to Ministral 3 8B); the config banner clears itself instead of persisting for the session; the active model is derived and never silently rewritten. Adds: resizable panel, blank URL disables that backend, TROUBLESHOOTING.md. Requires proxy v2.0.0.

proxy-v2.0.0 — hardened, auth-required gateway

Choose a tag to compare

@mabaeyens mabaeyens released this 26 Jul 08:53

Production-hardening release. Turns the demo-grade cm-llm-proxy into a hardened gateway that
holds the Anthropic key server-side and authenticates every caller by their Qlik session
(specs P01–P07). Breaking: the proxy now requires Qlik-session auth and env-based
credentials — a client can no longer just reach it and spend the key.

⚠️ Breaking

  • Caller authentication is now mandatory. Every /api/* request must present a valid Qlik
    session; unauthenticated requests are rejected 401 before any upstream call. Requires
    QLIK_SESSION_URL, QLIK_CERT, QLIK_KEY (and CORS QLIK_ORIGINS) — the proxy fails fast at
    boot without them.
  • The Anthropic key must be server-side (ANTHROPIC_API_KEY env); any client-supplied key
    header is stripped. There is no pass-through of a browser key anymore.

Added

  • Credential custody (P01): key from env, injected per request, client key headers stripped;
    bounded retry/backoff on transient upstream 429/5xx.
  • Caller authentication (P02): validates the Qlik session against QPS over mutual-TLS and
    resolves the real user id. Session carrier is the same-site X-Qlik-Session cookie (a named
    virtual proxy uses X-Qlik-Session-<prefix> via QLIK_SESSION_COOKIE); the x-qlik-session
    header is an explicit ticket override. Positive-only TTL cache.
  • Concurrency & resilience (P03): global + per-user in-flight ceilings, bounded FIFO queue
    (503 + Retry-After on overflow/timeout), streaming backpressure, keep-alive upstream pools,
    graceful drain on shutdown.
  • Input validation & model allowlist (P04): per-route body-schema validation, request-size
    caps, server-side model allowlist.
  • Transport & CORS hardening (P05): strict origin allowlist, security headers, TLS floor,
    per-IP rate limiting.
  • Observability & service (P06): structured JSON logs with level gating (LOG_LEVEL), a
    separate audit log (no bodies/secrets), /health · /ready · /metrics, boot-time config
    validation, and a Windows-service wrapper (auto-start/restart, log rotation). setup.ps1
    automates install.
  • Streaming on both routes (stream: true), piped through untouched as text/event-stream.
  • Client-cancel propagation: when the client disconnects (closed tab / the extension's Stop
    button), the upstream call is cancelled so the model stops generating — streaming destroys the
    piped stream, and the buffered path aborts the in-flight upstream call via an AbortSignal
    wired to the response close.
  • Tests & CI (P07): 102 node:test cases (integration over the real /api surface, load,
    drain), ESLint, GitHub Actions.

Fixed

  • QPS xrfkey (P02). Verified end-to-end against a live QSEoW node: QPS rejects any API call
    without an XSRF key (403 "XSRF prevention check failed"), which was mapped to AuthError and
    would have rejected every session, valid or not. The session-validation call now sends a
    16-char xrfkey in both the query string and the X-Qlik-Xrfkey header. Note: QLIK_SESSION_URL's
    host must match a SAN of the QPS certificate (QSEoW uses the short hostname, not the FQDN).

v0.5.1 — Stop button + concise local-model answers

Choose a tag to compare

@mabaeyens mabaeyens released this 25 Jul 15:05

Added

  • Stop button. A red “Stop” button appears in the panel while a response is generating and
    aborts it immediately, keeping any text produced so far and marking the message as stopped. It
    reuses the existing abort handle (streamed and buffered paths alike) — most useful for local
    models, which can generate for minutes. Stop actually halts inference, not just the UI: the
    browser aborts the request, and the proxy propagates the disconnect to the upstream so the model
    stops generating (streaming destroys the piped stream; the buffered path now aborts the in-flight
    upstream call via an AbortSignal wired to the response close, so a cancelled buffered request
    no longer runs to completion server-side).

Changed

  • Concise local-model answers. Local models (Ministral/Mistral) tend to be verbose and, at a
    few tokens/second, an over-long answer can run for minutes. A brevity instruction
    (config.API.LOCAL.SYSTEM_SUFFIX) is now appended to the system prompt on local calls only,
    telling the model to answer in a few bullets/sentences with no preamble or summary. Hosted models
    are unchanged.

v0.5.0 — production hardening: proxy-only, session auth, log levels

Choose a tag to compare

@mabaeyens mabaeyens released this 25 Jul 12:03

Production-hardening release. Turns the demo-grade extension + cm-llm-proxy into a hardened,
monorepo pair (proxy specs P01–P07, extension specs E01–E07). Fully unit-tested (30 extension +
96 proxy node:test cases) with CI on both artefacts.

⚠️ Breaking

  • Proxy is now mandatory; direct-from-browser mode is removed. The extension no longer stores
    or sends an Anthropic API key. Set a Proxy URL (and, for local models, a Local model URL)
    pointing at your deployed proxy. Existing instances that relied on the browser key / direct mode
    must switch to the proxy, which must be deployed with TLS and Qlik-session validation.
  • The API key property has been removed from the extension settings; the key lives only on the
    proxy.

Security

  • Credential custody (P01): the proxy holds the Anthropic key server-side (from env), strips any
    client-supplied key header, and injects the key itself. js/security.js and the bundled CryptoJS
    are deleted from the extension.
  • Caller authentication (P02): the proxy validates the caller's Qlik session (mutual-TLS to the
    Qlik Proxy/Repository API) before any upstream call. In the same-site deployment the browser sends
    the Qlik session cookie automatically (extension calls with credentials: 'include'); the
    proxy reads it from X-Qlik-Session (a named virtual proxy uses X-Qlik-Session-<prefix>, set via
    QLIK_SESSION_COOKIE). The x-qlik-session header stays as an explicit ticket override.
  • Input validation & model allowlist (P04): per-route body-schema validation, request-size caps,
    and a server-side model allowlist — invalid/oversize/disallowed requests are rejected before the
    upstream call.
  • Transport hardening (P05): strict CORS origin allowlist, security response headers, a modern
    TLS floor, and per-IP rate limiting.
  • Output sanitization (E04): audited every DOM sink; model output goes through DOMPurify
    (fail-closed) and all dynamic strings through escaping; streaming uses textContent.

Added

  • Concurrency & resilience (P03): admission control with global + per-user in-flight ceilings, a
    bounded FIFO queue (503 + Retry-After on overflow/timeout), streaming backpressure, upstream
    keep-alive pooling, and graceful drain on shutdown.
  • Automated proxy setup. proxy/scripts/setup.ps1 (idempotent) does the mechanical
    install steps on a Windows node — Node check, npm ci, optional self-signed dev cert +
    trust, .env scaffolding from the template with the values you pass, and optional
    Windows-service registration. Site secrets (API key, Qlik auth) are supplied by you, never
    invented.
  • Observability & service (P06): structured JSON request logs, a separate audit log
    (who-asked-what-when, no bodies/secrets), /health · /ready · /metrics, boot-time config
    validation, and a Windows-service wrapper (auto-start/restart, log rotation).
  • Client request lifecycle (E02): at most one in-flight request per widget — Submit/Suggest are
    disabled while busy, a unified abort handle covers both buffered and streamed paths, the app-context
    cache is race-safe, and a proxy 503 surfaces as a friendly "busy, try again" message.
  • Persistent floating widget. The panel is a body-global singleton that persists across sheet
    navigation, so you can move between sheets and select charts from any of them. A single-instance
    guard prevents duplicate widgets/listeners, and per-fetch engine session objects are
    created-read-destroyed inline, so nothing leaks. (teardown() helpers exist for a genuine teardown
    — page unload / tests — but are deliberately not wired to Qlik's per-sheet destroy, which
    would otherwise remove the widget every time you change sheets.)
  • Dev tooling & tests (E06/P07): dev-only package.json + ESLint + node:test suites (an AMD
    test harness for the pure modules; integration + load/drain harness for the proxy) and GitHub
    Actions CI, path-filtered per artefact. The shipped runtime stays plain AMD (no build step).
  • Log-verbosity levels. Both the proxy and the extension now support ERROR / WARN / INFO /
    DEBUG
    severity control. The proxy reads LOG_LEVEL from its .env (audit log unaffected — it
    is always written); the extension exposes a Log level setting in the properties panel that
    changes browser-console verbosity live. ERROR shows only errors; WARN adds warnings (incl. rejected
    requests); INFO adds one line per accepted action; DEBUG traces everything. Defaults to DEBUG for
    test/demo — lower it to quieten output for production.
  • Config & release hardening (E05/E07): all data-collection bounds centralised in config.DATA
    and validated at init; config validation surfaces problems in the panel; package.ps1 fails on a
    config.js/.qext version mismatch; a RELEASING.md checklist and scripts/pre-release-check.ps1
    guard the immutable-release rules (new tag only, matching versions, CHANGELOG entry).

Changed

  • Monorepo (M01): the proxy now lives in-tree under proxy/ (history preserved), versioned
    independently from the extension.
  • Data-collection caps (MAX_FETCH_CELLS, MAX_CELLS_PER_PAGE, FETCH_PAGE_CONCURRENCY,
    MAX_FIELDS, payload thresholds) are consolidated in config.DATA and reconciled with the proxy
    body-size limit, so an oversize payload is caught client-side with a friendly message.

Removed

  • js/security.js and js/lib/crypto-js.min.js (no browser-side key), the direct-browser transport
    and its x-api-key / anthropic-dangerous-direct-browser-access headers, and the API-key setting.

v0.4.0 — model picker in the chat panel, streamed answers, Ministral 3 3B

Choose a tag to compare

@mabaeyens mabaeyens released this 24 Jul 12:48

Added

  • In-panel model picker. A Pick model button sits next to Submit and Suggest a chart, listing every model with the active one ticked. Switching asks "This will clear your current conversation! Change model?" and resets the thread — conversation history can't meaningfully cross models.
  • The active model is always visible — under the submit row, and in each answer's footer next to LLM Token Usage. The footer credits the model that was active when the request was sent, so switching mid-flight can't mislabel an answer.
  • Streamed answers. Responses render token-by-token as they are generated, with a blinking caret. Toggle under Advanced Options → "Stream the answer as it is generated"; falls back to a buffered request when the browser or backend can't stream. Streaming chunks are inserted as plain text and the markdown is rendered once at the end — per-token markdown parsing flickers and renders half-written syntax as noise. In-flight streams are aborted on new chat / model switch.
  • Ministral 3 3B as a second local model — roughly half the memory of the 8B at comparable speed. Uses the derived tag ministral-3b-demo (FROM ministral-3:3b + PARAMETER num_ctx 8192); Ollama's 64k default context inflates the KV cache to ~10 GB and pushes the model almost entirely onto the CPU.

Changed

  • API.MODELS is now a registry of { id, label, hint, local, tag } driving both the properties dropdown and the in-panel picker, so the two can't drift apart. The properties dropdown is now the session default only — MODEL_LOCKED stops paint() (which runs on every selection event) from reverting a live choice.
  • "Suggest a chart" tolerates off-schema JSON from smaller models. Parsing is three escalating passes: strict → repair (comments, trailing commas, backticks) → regex salvage. Dimension/measure entries are coerced to strings.
  • The "No API key stored" notice is hidden while a local model is selected.
  • Properties-panel labels shortened with wrapping help text underneath — the sidebar clips labels rather than wrapping them.

Requires a proxy update

Streaming needs the matching cm-llm-proxy change: it buffered every response, which defeats streaming. Both /api/ollama and /api/anthropic must pipe the upstream body through untouched when stream: true is requested.

Install

Download AnthropicExtension-v0.4.0.zip and import it via QMC → Extensions (or unzip into the Qlik Sense Desktop Extensions folder).

v0.3.5 — suggested charts render again

Choose a tag to compare

@mabaeyens mabaeyens released this 24 Jul 11:55

Fixed

  • "Suggest a chart" failed to render with Could not render chart: Devhub.Cols.QdefOrQlibraryid, Devhub.Cols.QdefOrQlibraryid. Columns were passed to visualization.create() as bare strings, which Qlik's client-side column mapper rejects when it cannot match the token to a field or master-item id — one error per rejected column. Each column is now wrapped in an explicit definition object ({ qDef: { qFieldDefs: [...] } } for dimensions, { qDef: { qDef: '=...' } } for measures), which always satisfies the validator. Master-item resolution to the underlying field/expression is unchanged.
  • A chart render failure now logs the spec and the resolved columns to the browser console.
  • package.ps1 stamped the .qext description twice; the strip-pattern's literal em dash was mis-decoded under Windows PowerShell 5.1.

Install

Download AnthropicExtension-v0.3.5.zip and import it via QMC → Extensions (or unzip into the Qlik Sense Desktop Extensions folder).

v0.3.4 — Local Ministral 3 8B backend (via Ollama)

Choose a tag to compare

@mabaeyens mabaeyens released this 17 Jul 13:57

Adds a local model backend alongside Claude. Pick "Ministral 3 8B (local, via Ollama)" in the Model dropdown to analyze charts with a local model — no API key required.

Added

  • Local Ministral 3 8B backend via Ollama (OpenAI chat-completions format; parses choices[0].message.content).
  • Local model URL property (routes through the cm-llm-proxy /api/ollama HTTPS route to avoid mixed-content blocking on QSEoW).
  • 5-minute client timeout for local calls (local inference is slower than the hosted API).

Setup (demo machine)

ollama pull ministral-3:8b
printf 'FROM ministral-3:8b\nPARAMETER num_ctx 8192\n' > Modelfile
ollama create ministral-3-demo -f Modelfile

Requires the companion cm-llm-proxy /api/ollama route. Model license: Apache 2.0. Demo-grade — ~6–7 tok/s on a 4 GB GPU.

Build 25.