Changelog
All notable changes to Elyra Sjá are documented here. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
Planned
- Shared team database — point every seat on a Studio (team) license at one database, so projects, history, trends and monitors are shared across the team instead of living per-machine. The database location becomes configurable, with a server-backed option for safe concurrent multi-user access.
- Google Search Console / Bing integration (real ranking data).
- Crawl cancellation from the UI.
- Competitor comparison.
0.3.0 — 2026-07-24
The licensing release. License codes are now ed25519-signed and verified offline against a public key embedded in the app, so the binary contains no secret that could be turned into a keygen.
Upgrading: codes minted by the previous checksum scheme no longer validate. If you hold an older
ELYRA-…code, get a replacement at elyracode.com/sja — the app will keep running on its trial grace period until you activate it.
Changed
- Licenses are now ed25519-signed (issued and signed server-side by
elyracode.com), replacing the offline FNV-1a checksum scheme. Codes look like
SJA-<base64url(payload)>.<base64url(signature)>and carry plan, licensee and expiry claims; the app embeds only the public key, so the binary contains no secret and cannot be turned into a keygen. Expiry is enforced (exp == 0means perpetual), issued-at is sanity-checked against the clock, and unknown payload versions fail closed. Codes are case-sensitive, so activation no longer upper-cases the input. Keys minted by the retired scheme stop validating.
Documentation
- Licensing rewritten around the signed format: the token layout, every claim
(
v/id/plan/email/iat/exp, withexp = 0meaning perpetual), how a code is accepted, and how to rotate the signing key. - Releasing documents publishing to elyracode.com with
deploy_sja: the notarization gate, versioned filenames, the checksum sidecar, and the signedlatest.jsonthat drives the in-app updater. - Corrected the architecture map, which still described the retired checksum validator.
0.2.0 — 2026-07-24
A performance, security, and hardening release. No breaking changes to the user-facing workflow; the crawler is substantially faster and the AI file sandbox is now symlink-safe.
Performance
- One shared, pooled HTTP client (
src/http.rs) instead of building a newreqwest::Client(and rustls config) per request — a crawl previously paid a fresh TLS handshake for every page and everyrobots.txt/llms.txtprobe. - Origin facts cached per crawl:
robots.txt/llms.txtare probed once per origin instead of once per page (was 2 extra requests × every page). - Bounded-concurrency crawl (4 in flight, staggered starts) replaces the fully sequential loop with a fixed 250 ms sleep between pages.
- HTML parsing moved to
spawn_blockingso large DOMs no longer block the async runtime (and progress events). - Response bodies are capped (5 MB) and non-text content types are rejected, so a large or binary URL can't be pulled into memory and parsed as HTML.
list_reportsno longer ships report payloads — history lists return metadata and fetch a full report on demand viaget_report.- Database indexes on
project_id(+ a partial index for unread alerts) and WAL journal mode, so monitor writes and UI reads don't block each other. - Release profile (
lto = "thin",codegen-units = 1,strip). - Crawl reports slimmed over IPC: full per-page reports now live in a
crawl_pagestable and are fetched only when the user drills into a page, so loading a 100-page crawl no longer ships every check for every page. - History retention + vacuum at startup: keeps the 20 newest runs per URL and the 20 newest crawls per project past 90 days, caps alerts at 500, drops orphan page rows, and vacuums only when there is real space to reclaim.
Changed
- Row mapping via
sqlx::FromRowinstead of hand-writtentry_get("col").unwrap_or_default()chains (~50 → 17 call sites, and the remainder are single-columnRETURNING/ JSON payload reads). Decoding now fails loudly on schema drift rather than silently defaulting.- Booleans keep an explicit conversion: SQLite stores them as INTEGER and the
sqlx
Anydriver refuses to decode that intobool, somonitors/alertsmap through private row structs with i64 flags. - Added an integration test that runs the migrations and exercises every list query, so a wrong column name or type fails in CI instead of at runtime.
- Booleans keep an explicit conversion: SQLite stores them as INTEGER and the
sqlx
main.rssplit into focused modules (1460 → ~130 lines):app_paths,settings,licensing,projects,reports,trends,crawls,monitors,ai,export. Verified the registered command set is unchanged (33 commands).
Security
- Sandbox escape via symlinks closed: the AI agent's file tools normalized
paths lexically only, so a symlink inside the project could point at e.g.
~/.ssh. Paths are now canonicalized (resolving symlinks) and checked against the canonical project root. - PageSpeed API key moved to the Keychain, matching the AI key (it was stored in the settings JSON in SQLite).
- Exported reports are written to an owner-only (
0700) directory under Application Support instead of the shared/tmp, and pruned after a day — they can contain client data. - Release CI refuses to publish an unsigned update manifest if
UPDATER_PRIVATE_KEYis missing (previously it emitted"signature": ""). - Dropped the deprecated
codesign --deep(single-binary app has no nested code).
Added
- Offline test suite (16 tests) covering HTML parsing/word count, scoring and
grading, recipe weighting, sitemap
<loc>extraction, URL dedupe keys, robots.txt matching, crawl aggregation, license validation, and the file sandbox (including symlink escape). - CI workflow running
cargo clippy --all-targets -D warnings+cargo teston every push and pull request.
Fixed
- Word count no longer includes
<script>/<style>, which inflated content depth and let JS-heavy pages pass the check on minified bundles. - robots.txt matching now supports
*wildcards,$anchors, andAllow:overrides (longest match wins), and honoursCrawl-delay. - AI write-confirmation could hang forever:
write_fileawaited the UI's answer with no timeout, so a closed window leftrun_fixstuck and leaked the pending oneshot. Now times out after 3 minutes and cleans up. - Overlapping monitor ticks: the frontend timer started a new tick even if the previous one was still running (duplicate runs/alerts). Added a guard, and the interval is now cleared on destroy.
- Crawl dedupe: post-redirect URLs weren't marked visited, so a page could be
analyzed twice and skew duplicate-title detection. URL keys now also normalize
directory indexes (
/index.html). - Sitemap discovery is truncated early instead of collecting every
<loc>(a sitemap can hold 50k entries) into memory.
0.1.1 — 2026-07-24
Fixed
- App failed to launch after download: the SQLite path and migrations directory
were baked in at compile time (
CARGO_MANIFEST_DIR), pointing at the build machine. The database now lives in~/Library/Application Support/Elyra Sja, and migrations are embedded in the binary and staged there at startup.
0.1.0 — 2026-07-24
First release. A standalone, local-first SEO/GEO optimization workspace built on the Elyra Framework (Rust + Svelte 5), for Apple Silicon macOS.
Added
- Single-page analysis — fetch + parse a URL into 19 weighted, categorized checks across Meta, Structure, Social, GEO/AI, and Indexing, with an overall score (0–100) and grade.
- Performance / Core Web Vitals — optional 6th category via Google PageSpeed Insights (LCP, CLS, TBT, FCP, performance score); opt-in in Settings.
- Site crawl — sitemap +
robots.txtdiscovery and internal-link BFS, with robots disallow respect, a politeness delay, broken-page detection, and duplicate title/description detection, rolled up into a site report. - Projects & history — projects (name, site URL, local code path) and a full analysis history stored in local SQLite.
- Trends & comparison — score-over-time per URL with sparklines, and a run-to-run diff of fixed vs regressed checks.
- AI fix agent — send an optimized fix prompt to your model (OpenAI /
Anthropic) via
elyra::ai; with a project's local path set, the agent edits the real files through sandboxed file tools, with an optional per-write confirmation dialog. - Fix-prompt builder — click any issue to append its context-aware, copy-paste-ready fix prompt to a prompt field.
- Monitors & scheduling — recurring re-scans (hourly/6h/daily/weekly) that feed history and raise regression / error alerts with native notifications.
- Client-ready PDF reports — branded, print-optimized reports for a single page and for a whole-site crawl.
- Settings — AI provider/model + API key, PageSpeed, analysis defaults, agency branding, write-confirmation toggle, and license activation (⌘,).
- Commercial licensing — a 3-day trial, then an offline-validated license code (issued at elyracode.com/sja). Secrets (API key, license) are stored in the macOS Keychain.
- Auto-updater — ed25519-verified updates with a startup check and a "Check for Updates…" menu item.
- Distribution —
.icnsgeneration, DMG packaging, and a GitHub Actions release pipeline (build → sign → notarize → DMG → signed update manifest). - Theme — Tokyo Night palette with a Svelte-orange brand accent; dark/light/ auto.