Skip to content

Releases: m4xx101/cryptex-oss

v2.8.1

09 Jun 19:54

Choose a tag to compare

First published release of the 2.8 line. 2.8.0 was tagged but never shipped: the release build hung on the emulated arm64 image and was killed at the job timeout, so no image and no GitHub Release were ever produced. 2.8.1 fixes the release pipeline and carries every 2.8.0 change.

Fixed

  • Release pipeline no longer hangs on the emulated arm64 build. The multi-arch Docker build was a single --platform amd64,arm64 --push command; on tag refs its layer cache is cold, so arm64 rebuilt from scratch under QEMU emulation and stalled past the job timeout, taking the GitHub Release down with it. The build is now split: linux/amd64 builds natively and publishes by digest unconditionally (image tags and the Release are cut from this leg alone), while linux/arm64 builds best-effort under a hard time cap and is merged into the manifest only when it finishes. Per-architecture build caches keep repeat builds fast.

Note

  • 2.8.1 includes all of the 2.8.0 work: the v2.7.0 structured-output experiment and its three mutators were reverted for a clean technique set, the Adversarial Suffix and Glitch-Token labs gained run-against-target panels, the model picker is scoped to your configured providers, the tool-title rendering bug is fixed, and a mount-smoke test now gates interactivity in CI.

Image

  • ghcr.io/m4xx101/cryptex-oss:v2.8.1 (linux/amd64, plus linux/arm64 when the emulated build completes within its cap). :latest, :v2.8, :2.8 track this tag.

v2.7.1

31 May 14:47

Choose a tag to compare

Critical fix. v2.7.0 (and, it turns out, v2.6.0 / v2.6.1) shipped a client-side regression that left the app non-interactive in production: the page rendered, but model pickers would not open, expanders would not expand, and runs would not start.

Fixed

  • App non-interactive (an effect-update-depth loop crashed hydration). The Campaign front door (the home route) mirrored its goal into the shared cross-tool context with a reactive $effect (sharedContext.goal = goal). That setter spreads (reads) the persisted context object before reassigning it, and createPersistedState did not dedup writes, so the effect depended on the very value it wrote and looped until Svelte aborted with effect_update_depth_exceeded. The throw on mount of the home route wedged the Svelte client scheduler and left every interactive control across the SPA dead (the prerendered HTML still rendered, so it looked fine). Two coordinated fixes:
    • app/src/lib/components/tools/campaign/CampaignTool.svelte: the goal-to-context mirror now tracks only goal, performs the shared-store read+write inside untrack(), and skips no-op writes.
    • app/src/lib/stores/_persisted.svelte.ts: createPersistedState now dedups structurally-equal writes, a class-wide guard so no future effect can infinite-loop on a persisted store.
  • New regression test (app/src/lib/stores/__tests__/persisted-no-loop.svelte.test.ts) drives the exact loop pattern through $effect.root + flushSync and asserts it settles instead of throwing.

Process

  • Root cause of the miss: prior release verification only confirmed pages rendered (a prerendered CSR shell always renders) and never tested interactivity. The release gate now adds a mount/effect regression test and a live "open the model picker" check.

Image

  • ghcr.io/m4xx101/cryptex-oss:v2.7.1 (multi-arch linux/amd64 + linux/arm64). :latest, :v2.7, :2.7 track this tag.

v2.7.0

31 May 03:36

Choose a tag to compare

SOTA attack labs land, and the dead-on-frontier techniques get an honest Legacy flag (kept, never removed). The headline is a new prompt-level structured-output / control-plane attack lab, plus three 2025-2026 single-prompt mutators.

Added

  • Structured-output attack lab (/redteam/structured-output). Prompt-level BreakFun ("Trojan Schema", arXiv:2510.17904): an innocent extraction framing, a chain-of-thought distraction, and a schema the target auto-completes into compliance, so populating fields reads as a formatting task rather than a request for advice. Two kinds: Trojan Schema (4 schema surfaces, namely JSON-Schema draft-07, Python dataclass, TypeScript interface, and function-call args) and schema-coercion (a strict output contract where prose, hedges, and refusals are framed as schema-validation failures). Auto-pivot rotates kinds on refusal. Single-call test-against-target with a heuristic verdict (caveat preserved). 10 bundled MIT-licensed seed templates.
  • Campaign integration. A new structured:* strategy family (one single-local adapter per kind) and a Structured-output (BreakFun) bundle, both auto-flowing into Full sweep.
  • 3 SOTA single-prompt mutators in PromptCraft and the technique registry (they auto-surface as Campaign strategies): adversarial_poetry (constrained-verse rewrite, arXiv:2511.15304), bad_likert_judge (single-prompt Likert-calibration approximation, Palo Alto Unit 42; the full multi-turn flow is deferred to v2.9), and trojan_schema (single-prompt BreakFun, sharing the lab's builder).

Changed

  • Honest "Legacy" relabel pass (metadata only, nothing removed). Frontier models patched several 2021-2023 classes, and Cryptex now says so. The tab rail gains a 'legacy' status with an amber chip on AdvSuffix and Glitch; honesty lines were added to those tool descriptions, to the roleplay and sysprompt_extract mutator descriptions, and as a category note on /transforms and /decode (plain single-layer ciphers and encodings are reversible primitives, effective stacked as layers, not standalone frontier bypasses). Every technique stays fully functional as an educational primitive and a composable layer; red_team_persona was deliberately left unflagged (it remains a viable layer inside multi_layer_attack).

Deferred

  • True constrained-decoding (EnumAttack / DictAttack, arXiv:2503.24191) needs a gateway response_format passthrough the BYOK chat gateway does not yet expose; it is sequenced for v2.9 behind that enabler. The "Toy to Tool" UX backbone (live dataset import, goal-by-model transfer matrix, JailbreakBench artifact export, OWASP / MITRE-ATLAS tagging) is the v2.8 milestone.

Tests

  • 942 / 942 unit tests pass (906 baseline plus 36 new); 0 type-check errors.

Image

  • ghcr.io/m4xx101/cryptex-oss:v2.7.0 (multi-arch linux/amd64 plus linux/arm64). :latest, :v2.7, :2.7, :v2, :2 all point at the same SHA.

v2.6.1

30 May 20:49

Choose a tag to compare

Fixes the intermittent "a tool sometimes gets stuck / its list never loads, and a page reload fixes it" bug on the GitHub Pages site.

Fixed

  • Stale-chunk hang after a deploy (root cause). Cryptex is a pure client-rendered SPA (ssr = false, prerender = true) served from GitHub Pages and redeployed often. Every deploy rehashes the lazily-imported JS chunk filenames; a browser holding an older app shell then tries to import() a chunk whose name no longer exists → 404 → the route never mounts → the tool stays blank → manual reload (fresh shell) fixes it. The non-determinism ("sometimes", "many tools", "reload fixes it") is the signature of this version-skew. Three coordinated fixes:
    • app/src/hooks.client.ts (new): a vite:preloadError listener does a one-time guarded location.reload() the instant a chunk import 404s, so a stuck tool becomes a brief auto-refresh onto the fresh build instead of a dead end. Plus a handleError client hook that logs any other failure mode instead of letting it hang silently.
    • svelte.config.js: kit.version.pollInterval = 60_000 — SvelteKit polls _app/version.json (already unique per build) and flips the updated store when a new deploy is detected.
    • app/src/routes/+layout.svelte: a beforeNavigate guard forces a full-page navigation when updated.current is true, so a client that already knows it is stale never attempts a dead client-side import.
  • GitHub Pages deep-link 404. The static adapter fallback was index.html, but GitHub Pages serves 404.html for unresolved paths. Changed the fallback to 404.html so deep-links and no-trailing-slash URLs (e.g. /cryptex-oss/emoji) fall through to the SPA shell and route correctly instead of hitting GitHub's own 404 page.

Changed

  • app/src/lib/workers/runInWorker.ts: added a 30s worker-reply timeout (defensive — terminates and surfaces a retryable error instead of awaiting forever if a worker ever loads but never posts back). Not implicated in the reported bug; belt-and-suspenders.
  • svelte.config.js: removed the dead $legacy: '../js' alias (the js/ directory was deleted in v2.4.1).

Ruled out (investigated, not the cause)

  • Web Worker URL resolution under the base path — pool.ts uses the Vite-native new Worker(new URL(..., import.meta.url)) pattern, which Vite rewrites to a correct base-aware hashed URL; workers also only engage for ≥50 KB inputs (never on tool-open).
  • Per-tool init / empty lists — Vault seeds load via eager import.meta.glob (bundled, never fetched); no await-forever on open.

Tests

  • 906 / 906 unit tests still pass; 0 type-check errors.

Image

  • ghcr.io/m4xx101/cryptex-oss:v2.6.1 (multi-arch linux/amd64 + linux/arm64). :latest, :v2.6, :2.6, :v2, :2 all point at the same SHA.

v2.6.0

29 May 20:05

Choose a tag to compare

The toolbox becomes a pipeline. Cryptex now has a Campaign front door — one page where you type a goal, pick a target once, and the tool fans your goal across many attack strategies, judges each with an LLM judge, and shows a graded ASR report. This is the shape every serious red-team framework (garak, PyRIT, promptfoo, DeepTeam) converges on, built entirely on parts Cryptex already shipped. The 26 individual tools remain as power-user escape hatches.

Added

  • /campaign — the Campaign front door, now the home landing page. Type a goal + pick a target + pick a technique bundle → Run. Strategies fan out through a bounded worker-pool (3 at a time, so multi-turn orchestrators don't storm provider rate limits), each result is judged, and an ASR-by-strategy report streams in live with the winner highlighted, expandable payload/response/judge-reasoning per row, Export campaign JSON, Save winner to Vault, and Re-run vs another model (side-by-side via local snapshots). Cold-start aware: with no provider configured the page onboards (NoProviderBanner + "Configure a provider" CTA) instead of looking broken.
  • Strategy adapter layer (app/src/lib/campaign/). The 26 tools collapse into three archetypes — single-local builders (reasoning kinds, SEAL cipher presets, Response-Attack priming styles, local-template mutators), single-llm techniques (registry mutators that generate via an LLM), and multi-turn orchestrators (TAP/PAIR/Crescendo/Many-Shot) — unified behind one CampaignStrategy.run(ctx) contract. Six technique bundles: Quick, Reasoning models, Cipher stack, Response priming, Multi-turn orchestrators, Full sweep. Abliteration is deliberately excluded (it's a model-profiling suite, not a goal attack).
  • Shared campaign judge (LLM-default, heuristic fallback). Wraps the StrongREJECT scorer: LLM-as-judge by default, falls back to the regex heuristic on parse-failure / rate-limit, re-throws AbortError. Returns bypassed | partial | refused + score + reasoning. Heuristic, not the trained paper classifier — caveat preserved in the report.
  • Cross-tool context bridge. A new shared cryptex.context store ({goal, targetModel}) plus a ContextBridge component wired into PromptCraft, Reasoning-attack, and Stacked-cipher: "Send to Campaign →" pushes a tool's goal+target into the front door, and "Use campaign goal/target" hydrates a tool from it. Explicit user actions only — never reactive, fully back-compat (no existing per-tool pref is touched).

Fixed

  • Mobile: Guide, About, and Settings were unreachable. The v2.5.0 hamburger drawer listed only tools, and the Guide header link was hidden below the sm breakpoint. Added a "More" section to the mobile drawer (Guide / About / Settings) and made the Guide button visible on mobile. The Settings sidebar no longer horizontal-scrolls (stacks vertically on phones).

Changed

  • Home route / redirects to /campaign (was /transforms).
  • TabRail gains a Campaign tab (first position, Rocket icon); it auto-propagates to the mobile drawer and the global running badge via the shared tabs array.
  • README, About page tool count (25 → 26), and the CI smoke-test (/campaign/ probe) updated. docs/USAGE.md opens with a new Recipe 0: Campaign — one-shot red-team.

Tests

  • 886 → 906 (+20): shared-context store (4), campaign judge (7), strategy adapters (6), campaign runner — bounded concurrency, error isolation, single history write (3).

Image

  • ghcr.io/m4xx101/cryptex-oss:v2.6.0 (multi-arch linux/amd64 + linux/arm64). :latest, :v2.6, :2.6, :v2, :2 all point at the same SHA.

v2.5.0

27 May 19:35

Choose a tag to compare

Production release: pipeline polish, mobile-responsive shell, multi-tool visibility.

Fixed

  • docker pull :latest returned stale code. :latest was pushed from BOTH the tag-build job AND the main-branch build job in .github/workflows/docker.yml. A docs-only push to main after a release tag would race the tag job and overwrite :latest with main's older HEAD. Removed the main-branch line; :latest now ONLY tracks the most recent semver tag.
  • GitHub Releases stuck at v1.0.1. Tags v2.0.0..v2.4.2 existed in git but no workflow ever created Release entries. Added softprops/action-gh-release@v2 step gated on tag push; release body extracted from the matching [X.Y.Z] CHANGELOG section via awk. Every future tag auto-publishes a Release with formatted notes.
  • No URL printed on container startup. Added docker-entrypoint.sh that prints a banner with the open URL before handing off to nginx. Honest about host-port mapping (doesn't print a misleading localhost:80 when users docker run -p 8080:80).

Added

  • In-app version display. Footer chip shows v{APP_VERSION} linking to the changelog. About page shows "Running v{APP_VERSION}" with View changelog + All releases links. Version is injected at build time via Vite define: reading the root package.json — no hand-sync.
  • Update-available banner. /settings/release/update-check.ts fetches api.github.com/.../releases/latest once per session (6h sessionStorage cache) and surfaces a dismissible accent banner above the HeaderBar when a newer version exists. Per-version dismiss state in localStorage so dismissing v2.5.1 doesn't suppress v2.6.0. Silent on every failure path (network, 4xx, malformed JSON, CORS). Default ON; opt-out via the new "Release notifications" section in Settings → Local Data.
  • Mobile-responsive shell.
    • New hamburger button (<sm only) in HeaderBar opens a full-screen MobileNavDrawer containing the same 26-tool list the desktop TabRail renders. Each row shows the same animate-pulse running-dot — the "blinking effect" works on mobile.
    • Drawer accessibility: role="dialog", aria-modal, focus trap, Escape to close, backdrop click closes, focus restores to the hamburger button.
    • Desktop TabRail wrapped in hidden sm:block so it disappears below 640 px (where it was wrapping to 3-5 rows and pushing the running dots off-screen).
    • Touch targets in HeaderBar buttons + Settings sidebar bumped to min-h-11 / h-10 w-10 on mobile (closer to the 44 px HIG floor) and back to the desktop sizing on sm: and up.
    • pt-safe / pb-safe / pl-safe / pr-safe utilities in app/src/app.css using env(safe-area-inset-*) — applied to the sticky header (iPhone notch) and the footer (Android nav bar).
  • Global "{N} running" badge in HeaderBar. ActiveRunsBadge.svelte surfaces in-flight runs from every tool, not just the currently-open route. Click opens a popover listing each running tool with elapsed time + a deep-link back to its route. Closes the visibility gap where users couldn't tell whether a long-running tool was still working after they navigated away.
  • Tool registry refactor. TabRail.svelte's TABS array now carries a toolId field per entry inside a <script module> export. MobileNavDrawer and ActiveRunsBadge both consume the same source-of-truth array. The old 9-entry hrefToToolId() switch is gone — all 26 visible tool routes now resolve to a toolId, so the desktop TabRail running-dot and the mobile drawer running-dot fire for every tool with an activeRuns.start() registration.
  • Reload-orphan notification. activeRuns.svelte.ts writes a tiny Set<toolId> snapshot to localStorage on every start() and removes on every finish/fail/cancel/clear. If the previous session left entries (closed tab, hard reload, crash mid-flight), the layout onMount emits a one-shot notify.info "N runs were interrupted by your last page reload" and clears the snapshot. The in-flight Promise can't be resumed, but the user's form state IS persisted (existing useToolState).
  • Dynamic README badge. README.md line 21 swapped its hardcoded v2.3.0 badge for a shields.io/github/v/release dynamic badge that polls GitHub's API and serves a cached SVG. The README never drifts from the actual release surface again.

Changed

  • app/vite.config.ts: build now reads root package.json via fs.readFileSync and exposes __APP_VERSION__ + __APP_REPO__ as compile-time globals. Also dropped the stale $legacy: '../js' alias that pointed at the deleted directory.

Tests

  • 873 → 886 (+13): full coverage for the new release/update-check.ts module — semver compare edge cases, sessionStorage cache hit/miss, silent failure on non-OK responses, thrown fetch, malformed JSON, manual cache clearing.

Image

  • ghcr.io/m4xx101/cryptex-oss:v2.5.0 (multi-arch linux/amd64 + linux/arm64). :latest, :v2.5, :2.5, :v2, :2 all point at the same SHA. The new pipeline guarantees :latest tracks the v2.5.0 SHA going forward — the bug that caused users to pull stale code is fixed.

v1.0.1

09 May 01:23

Choose a tag to compare

Bug-fix and feature release.

Highlights:

  • Session persistence: long-running tools (probe-lab, cross-model-diff, harmbench, strongreject, jbb, replayer, fingerprinter, promptcraft, anticlassifier) now continue running when you switch tabs. The TabRail shows a pulsing indicator on tabs with active runs.
  • 5 new providers: Ollama, LM Studio, vLLM, Llama.cpp (local servers) and NVIDIA NIM (cloud). All speak the OpenAI-compat protocol; configure in Settings -> Providers.
  • 0 npm audit vulnerabilities (was 12). Vite, SvelteKit, and vitest upgraded to latest.
  • Documentation cleanup: neutral framing throughout README and CLAUDE.md.

Hotfix (within hours of v1.0.1 release): Regenerated app/package-lock.json to fix 'npm ci' missing entries for react/react-dom/scheduler (transitive optional peers of the 'ai' SDK). Affected 'npm run build' from root and Docker builds.