Architecture
Rust backend (Yggdrasil) ↔ Svelte 5 frontend (crown) over the Elyra Framework's
typed MessagePack bridge. The Rust binary embeds the built frontend and serves
it from elyra://localhost.
elyra-sja/
├── src/
│ ├── main.rs # App wiring only (~130 lines)
│ ├── app_paths.rs # data dir, DB URL, migration staging, retention/vacuum
│ ├── http.rs # one shared, pooled HTTP client + capped body reads
│ ├── cli.rs # headless `check` subcommand (no window, no database)
│ ├── mcp.rs # MCP server on stdio, read-only tools (see docs/mcp.md)
│ ├── seo.rs # single-page analysis engine (GUI-free): checks + scoring
│ ├── extract.rs # the page as an engine extracts it: reader view, passages
│ ├── schema.rs # JSON-LD read for completeness, publisher entity, dates
│ ├── dates.rs # ISO 8601 / HTTP-date / log timestamps → days (no crate)
│ ├── llmstxt.rs # llms.txt parser (title, summary, links; lists a URL?)
│ ├── intl.rs # hreflang / language-tag validation (GUI-free)
│ ├── robots.rs # robots.txt: every user-agent group, not just `*`
│ ├── botlog.rs # AI-crawler log analysis engine (GUI-free): per bot, per day
│ ├── iprange.rs # published crawler IP ranges: fetch shape, CIDR, verdicts
│ ├── citations.rs # citation probe: ask ChatGPT/Claude/Perplexity, judge answers
│ ├── generate.rs # llms.txt + JSON-LD from crawl data (GUI-free)
│ ├── pagespeed.rs # Core Web Vitals via PageSpeed Insights
│ ├── crawl.rs # site crawler (sitemap + BFS, robots, aggregation)
│ ├── fs_tools.rs # sandboxed AI file tools + atomic, confirmed writes
│ ├── license.rs # offline license validation (ed25519 signature)
│ ├── gsc.rs # Search Console OAuth (PKCE + loopback) + API client
│ ├── secrets.rs # macOS Keychain wrapper
│ └── <commands> # settings, licensing, projects, reports, trends, crawls,
│ # botlogs, probes, monitors, ai, export, search_console
├── migrations/ # SQLite migrations (0001–0010; embedded)
├── app/ # Vite + Svelte 5 frontend
│ └── src/
│ ├── App.svelte # shell, nav, views, scheduler timer
│ ├── bindings.ts # generated by `rata codegen` — do not edit
│ └── lib/ # Report, SiteReport, Trends, Monitors, locales,
│ # Settings, Lock, ProjectDialog, Icon, …
├── scripts/ # icns, dmg, updater keygen/sign
├── vendor/elyra-framework # the framework, as a git submodule
└── .github/workflows/release.yml
Commands (typed bridge)
Frontend calls Rust through the generated api.* facade. Groups:
- Analysis —
analyze_page,analyze_and_save,get_report,list_reports,recent_reports,delete_report. - Crawl —
crawl_site,cancel_crawl,list_crawls,get_crawl,get_crawl_page,delete_crawl. - AI crawlers —
analyze_bot_log,unknown_bot_agents,verify_bot_ips(the log's addresses against the operators' published ranges),bot_visit_trends(visits per crawler per day for a project). - Citation probe —
probe_engines_available,probe_citations,list_citations,delete_citation,citation_summary. - Generators —
generate_llms_txt,generate_faq_schema,generate_article_schema,save_generated. - Trends —
url_trends,compare_reports. - Projects —
list_projects,create_project,update_project,delete_project. - Monitors —
tick_monitors,run_monitor_now,list_monitors,create_monitor,toggle_monitor,delete_monitor,list_alerts,unread_alerts,mark_alerts_read. - AI —
run_fix,respond_write. - Search Console —
gsc_status,gsc_connect,gsc_disconnect,gsc_properties. - Settings & license —
get_settings,save_settings,license_status,activate_license. - Export —
open_report.
After changing any
#[command]signature or aspectatype, re-runrata codegenbefore touching the frontend.
Events (Rust → frontend)
Pushed over channels the frontend subscribes to:
crawl— crawl progress (status / per-page / done).ai:fix— AI agent activity (status / tool / write).ai:confirm— pending write-confirmation requests.
Database (SQLite)
| Table | Purpose |
|---|---|
projects |
name, site_url, local_path, created_at |
reports |
per-run page report (JSON payload) + score |
crawls |
per-crawl site report (slim: aggregates + page summaries) |
crawl_pages |
full per-page report for a crawl, fetched on drill-in |
settings |
key/value: app (settings JSON) and license (first-run) |
monitors |
scheduled scans (url, recipe, interval, last run/score) |
alerts |
regression / error / improvement events |
citations |
one row per citation probe of one engine: cited?, position, cited URLs, excerpt, error |
bot_visits |
AI-crawler visits per project, day, crawler and log file (two files add up; the same file replaces) |
The reports payload is a PageReport, which since 0.6.0 also carries the
extracted reader view (reader), the ranked passages (passages) and the
publisher entity (entity); older payloads load with those defaulted.
Secrets are not in the database — see Security.