Elyra
Elyra The coding agent eTerm The terminal that knows where each command ends Huskarl AI colleagues on your own Mac Starf An activity monitor for Apple silicon that never invents a number etrans An SSH and SFTP client for macOS Elyra VM Virtual machines for macOS, Linux and Windows on your Mac Litr A small, native web browser for macOS Notr A notebook for macOS e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Refr Local-first PDF workspace for macOS Elyra Workspace A desktop workspace for coding agents Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Release notes
Changelog
Elyra
Shared substrate (`substrate-core`)

Shared substrate (substrate-core)

The substrate-core crate is the contract behind the "one ecosystem" facades. It defines backend-agnostic Cache, Storage, and Queue traits — the same operations you reach for on the Elyra (Rust + Svelte desktop) side and the Askr (Rust PHP server, Laravel) side — so the two worlds share a single, verifiable contract while each keeps a backend that fits its environment.

substrate-core            traits: Cache · Storage · Queue  (+ Error/Result)
   ├── Elyra   implements them over local backends  (this repo)
   └── Askr    implements them over its server substrate

The contract

Tiny, dependency-free, std-only. Values are opaque bytes — the traits fix the operations and semantics, not a cross-language value encoding (a PHP-serialized value and a JSON one differ), so each side picks its own encoding on top.

pub trait Cache {
    fn get(&self, key: &str) -> Option<Vec<u8>>;
    fn put(&self, key: &str, value: &[u8], ttl: Option<Duration>);
    fn add(&self, key: &str, value: &[u8], ttl: Option<Duration>) -> bool;
    fn has(&self, key: &str) -> bool { /* default */ }
    fn forget(&self, key: &str) -> bool;
    fn increment(&self, key: &str, delta: i64) -> i64;
    fn decrement(&self, key: &str, delta: i64) -> i64 { /* default */ }
    fn flush(&self);
}

pub trait Storage {
    fn put(&self, path: &str, contents: &[u8]) -> Result<()>;
    fn get(&self, path: &str) -> Result<Vec<u8>>;
    fn exists(&self, path: &str) -> bool;
    fn delete(&self, path: &str) -> Result<()>;
    fn size(&self, path: &str) -> Result<u64>;
    fn files(&self, dir: &str) -> Result<Vec<String>>;
}

pub trait Queue {
    fn push(&self, job: &str, payload: &[u8]);
}

Elyra's conformance

Elyra's facades implement these traits (re-exported as elyra::substrate), so generic code can be written against the contract:

use elyra::substrate::Cache as CacheContract;

fn warm(cache: &impl CacheContract) {
    cache.put("ready", b"true", None);
}
// works with elyra::cache::Cache — and with any Askr-side Cache

The ergonomic Cache (JSON values, remember, typed helpers), Storage, and Queue APIs are sugar layered on top of the same byte-level contract; Cache stores bytes internally, so substrate get/put round-trip losslessly. Conformance is verified in framework/tests/substrate.rs.

Backends per world

Facade Elyra Askr / Laravel
Cache in-process TTL store shared-memory region
Storage jailed local disk local / S3 disk
Queue in-process background jobs supervised worker fleet

Consuming it from Askr (no crates.io needed)

substrate-core lives in this repo, so the Askr side depends on it directly as a git dependency pinned to a release tag — no crates.io publishing required:

# Askr's Cargo.toml
substrate-core = { git = "https://github.com/kwhorne/elyra-framework", tag = "v0.5.1" }

Cargo resolves the substrate-core package from within this workspace. Askr then implements the same Cache / Storage / Queue traits over its server substrate, and both worlds share one source of truth for the API shape.

Tradeoff: the contract's version tracks Elyra's release tags, so bump the pin when the traits change. Publishing to crates.io only becomes worthwhile if a third party (outside Elyra + Askr) needs the contract.

Design rule

Keep substrate-core small and backend-agnostic. It must express what (get/put/ttl, blob I/O, enqueue), never how — no fork, shared-memory, or filesystem assumptions — or it would lock one world out. New operations are added only when both worlds can implement them.

Related