Repository navigation
Releases: mabaeyens/AnthropicExtension
Release list
v0.5.5
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.jsAPI.MODELSandAPI.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
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 localcerts/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:
- 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. - 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.
- A dedicated local development CA (
- Trust model improvement:
-TrustCertnow 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
-CertHostsis now a comma-separated string parameter, not a PowerShell array (an array-typed parameter does not reliably survive apwsh -Fileinvocation from outside PowerShell).- Documented:
npm cideletes thenode-windowsservice-wrapper dependency (deliberately excluded frompackage-lock.json), which crashes an already-installed Windows service with Error 1067 on its next restart. Fix:npm install --no-save node-windows, thenRestart-Service <name>.
Pairs with extension v0.5.5.
See CHANGELOG.md for full details.
proxy-v2.0.1
Fixed
- Default
ALLOWED_ANTHROPIC_MODELSallowlist 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 withALLOWED_ANTHROPIC_MODELSset explicitly in its own.envmust update that value too, or it will403 Model not allowedthe new IDs.
Added
SERVICE_NAMEenv var forservice/install-service.js/service/uninstall-service.js, so an operator can register the Windows service under a site-specific name instead of the hardcodedcm-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
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
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 rejected401before any upstream call. Requires
QLIK_SESSION_URL,QLIK_CERT,QLIK_KEY(and CORSQLIK_ORIGINS) — the proxy fails fast at
boot without them. - The Anthropic key must be server-side (
ANTHROPIC_API_KEYenv); 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-siteX-Qlik-Sessioncookie (a named
virtual proxy usesX-Qlik-Session-<prefix>viaQLIK_SESSION_COOKIE); thex-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-Afteron 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 astext/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 anAbortSignal
wired to the responseclose. - Tests & CI (P07): 102
node:testcases (integration over the real/apisurface, 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 toAuthErrorand
would have rejected every session, valid or not. The session-validation call now sends a
16-char xrfkey in both the query string and theX-Qlik-Xrfkeyheader. 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
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 anAbortSignalwired to the responseclose, 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
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.jsand 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 withcredentials: 'include'); the
proxy reads it fromX-Qlik-Session(a named virtual proxy usesX-Qlik-Session-<prefix>, set via
QLIK_SESSION_COOKIE). Thex-qlik-sessionheader 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 usestextContent.
Added
- Concurrency & resilience (P03): admission control with global + per-user in-flight ceilings, a
bounded FIFO queue (503+Retry-Afteron 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,.envscaffolding 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 proxy503surfaces 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-sheetdestroy, which
would otherwise remove the widget every time you change sheets.) - Dev tooling & tests (E06/P07): dev-only
package.json+ ESLint +node:testsuites (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 readsLOG_LEVELfrom 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.ps1fails on a
config.js/.qextversion mismatch; aRELEASING.mdchecklist andscripts/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 inconfig.DATAand reconciled with the proxy
body-size limit, so an oversize payload is caught client-side with a friendly message.
Removed
js/security.jsandjs/lib/crypto-js.min.js(no browser-side key), the direct-browser transport
and itsx-api-key/anthropic-dangerous-direct-browser-accessheaders, and the API-key setting.
v0.4.0 — model picker in the chat panel, streamed answers, Ministral 3 3B
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.MODELSis 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_LOCKEDstopspaint()(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
Fixed
- "Suggest a chart" failed to render with
Could not render chart: Devhub.Cols.QdefOrQlibraryid, Devhub.Cols.QdefOrQlibraryid. Columns were passed tovisualization.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.ps1stamped the.qextdescription 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)
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/ollamaHTTPS 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.