Schema-driven code generation and RPC framework for Hero distributed services.
  • Rust 92.4%
  • JavaScript 4.7%
  • HTML 2.7%
  • Makefile 0.2%
Find a file
Timur Gordon b63e0fec1e
Some checks failed
Test / test (push) Failing after 42s
lab publish / publish (push) Failing after 16m57s
feat(generator): wire approach-B OSIS scaffolder + recover osis/generator build
Makes `lab blueprint --schemas-dir` emit a compiling, runnable approach-B
service workspace (core + server + sdk) and recovers the generator/osis
crates that despiegk's hero-lib-base merge left half-broken on development.

Generator:
- Wire the WorkspaceScaffolder to the openrpc_server! server emitter:
  generate_server_crate now emits main.rs + <dom>_impl.rs via
  generate::server_main, an oschema-backed Cargo.toml (no jsonrpsee/hero_rpc2),
  per-domain rpc_<dom>.sock service.toml, and copies the schema tree to oschema/.
- Port the SDK emitter to herolib_macros::openrpc_client! over herolib_openrpc
  (dropped the deleted hero_rpc_derive/hero_rpc2 + the rhai sibling).
- Port the tests-crate emitter to drive the typed SDK client (connect_socket),
  and the core crate Cargo.toml off the deleted hero_rpc2.
- server_main: store structs now mirror the macro WIRE types (wire_type():
  every integer width -> i64, floats -> f64, str -> String, optionals -> Option)
  so the CRUD arms convert wire<->store by identity move; resolve the OSIS
  data dir under PATH_ROOT at runtime.
- Re-export the server_main emitter API from the crate root.
- Restore the workspace [workspace.dependencies] the emitters reference.
- Update unit tests + openrpc tests to the tightened contract (underscored
  CRUD, no _new, single-param methods, no schema-declared sid).

osis (hero_rpc_osis): recover the build after the bad merge —
- find is first-class (`pub mod find;` off src/find.rs, not the deleted
  hero_rpc2::find re-export); remove the dangling `pub mod rpc;`.
- sid re-export sources herolib_oschema::types::SmartId (the SmartId the
  OsisObject derive + DBTyped are defined over), fixing wire<->store types.
- _regex_for_codegen always available (generated FindParams use it).

Workspace: re-enable generator/blueprinter/osis/openrpc_http_client_lib;
rename herolib_derive -> herolib_macros throughout; drop the dead
hero_rpc_derive crate (deleted upstream, resurrected by the bad merge).

Gate: `cargo test -p hero_rpc_generator` green (151) +
`--ignored emitted_main_compiles` green; a scaffolded hero_crm server boots
and round-trips CRUD through OSIS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 08:11:45 +02:00
.cargo refactor: applied linter fixes + fmt 2026-04-02 17:03:27 +02:00
.forgejo/workflows ci: install lab onto PATH from ~/.local/bin in the publish workflow 2026-05-29 15:38:36 -04:00
.hero chore: fix stale paths, exclude _archive from workspace, update getting-started guide 2026-05-29 21:50:09 +02:00
benches chore(workspace): add rust-version = 1.96 to all crates, drop herolib_web dep 2026-06-01 09:32:15 +02:00
blueprints/service feat(generator): wire approach-B OSIS scaffolder + recover osis/generator build 2026-06-07 08:11:45 +02:00
crates feat(generator): wire approach-B OSIS scaffolder + recover osis/generator build 2026-06-07 08:11:45 +02:00
docs chore: fix stale paths, exclude _archive from workspace, update getting-started guide 2026-05-29 21:50:09 +02:00
examples docs(examples/crm): exercise [T] = [] defaults in Company + Person 2026-05-30 01:50:25 +02:00
tests feat(generator): wire approach-B OSIS scaffolder + recover osis/generator build 2026-06-07 08:11:45 +02:00
.gitignore chore: remove Cargo.lock and update gitignore 2026-06-06 08:05:25 +02:00
BENCH_RESULTS.md hero_rpc#129: scaffolder examples/ + benches/ + serial_test dev-dep + osis_benches symmetry (#139) 2026-05-25 10:47:22 +00:00
Cargo.toml feat(generator): wire approach-B OSIS scaffolder + recover osis/generator build 2026-06-07 08:11:45 +02:00
GETTING_STARTED.md docs: update README + GETTING_STARTED for the hero_lib-base architecture 2026-05-31 06:42:54 +02:00
OPTIMIZATION_FINDINGS.md chore(blueprints): dependency optimization — fix URL drift, drop bloat, slim tokio 2026-05-29 11:18:21 +02:00
README.md docs: update README + GETTING_STARTED for the hero_lib-base architecture 2026-05-31 06:42:54 +02:00
REFACTOR_INSTRUCTIONS.md docs(refactor): phase 6 complete — workspace fully green on hero_lib 2026-05-31 06:53:29 +02:00

Hero RPC

Schema-driven code generation and scaffolding for building distributed Hero services. Define your data model in .oschema files, and Hero RPC scaffolds a complete workspace: SmartID storage types, an OSIS-backed JSON-RPC server, an admin dashboard, a public web surface, and a typed client SDK — all built on the hero_lib runtime + macros (herolib_derive::openrpc_server! / openrpc_client!, hero_lifecycle, herolib_oschema_server).

Architecture (post hero_lib-base refactor): this repo no longer ships its own RPC transport or server runtime. The RPC surface — wire types, the service trait, dispatch, the serve_domains launcher, and the client transport — is generated by hero_lib's macros and run by hero_lifecycle. This repo owns only what is unique to it: the OSIS data+index layer, the schema-driven code generator, and the blueprint templates. See REFACTOR_INSTRUCTIONS.md.

Packages

Crate Description
hero_rpc_osis OSIS — the blueprint-unique data layer: SmartID storage (DBTyped), OTOML serialization, the OSchema language re-exports, and an optional index feature for full-text _find via the hero_indexer service. No RPC/server runtime (that lives in hero_lib).
hero_rpc_generator Schema-driven workspace scaffolder library — emits the full Hero service tree (server + admin + web + sdk) from .oschema files, targeting hero_lib's openrpc_server!.
blueprinter Tera-based template engine the lab blueprint CLI runs on; consumes either the template tree at blueprints/ or the schema-driven path (delegates to hero_rpc_generator).
hero_rpc_client Cross-platform HTTP/WASM RPC client for generated services (native transport via herolib_openrpc).

The RPC macros (openrpc_server!, openrpc_client!, rhai_openrpc!, #[derive(OsisObject)]) and the server/transport runtime come from hero_lib (herolib_derive, hero_lifecycle, herolib_oschema_server, herolib_openrpc).

How It Works

1. Define your schema

Create .oschema files that describe your data model:

Difficulty = "easy" | "medium" | "hard"

# Recipe [rootobject]
Recipe = {
    sid: str
    @index
    name: str
    @index
    description: str
    difficulty: Difficulty
    prep_time: u32
    ingredients: [str]
    created_at: otime
}

service RecipeService {
    version: "1.0.0"
    description: "Recipe management"

    get_by_category(category: Category) -> [Recipe]
}

2. Scaffold a workspace

Generate a complete 6-crate workspace (lib + server + admin + web + sdk

  • benches + tests + .forgejo) from your schemas via lab (from hero_skills):
lab blueprint --dst ./hero_recipes --name hero_recipes --schemas-dir schemas

The schema-driven path runs whenever --schemas-dir is set; without it lab blueprint falls back to the Tera template tree under blueprints/service/ (the bare service skeleton). Either way the work is done by libraries exposed by this repo — hero_rpc_generator::build::WorkspaceScaffolder for the schema path and blueprinter::expand for the template path. lab just drives the CLI; the engines and the canonical templates live here.

This creates:

hero_recipes/
├── Cargo.toml                          # Workspace root
├── schemas/
│   └── recipes/recipes.oschema
├── crates/
│   ├── hero_recipes/                   # Core types + DB persistence
│   │   ├── Cargo.toml
│   │   └── build.rs                    # Code generation on cargo build
│   ├── hero_recipes_server/            # JSON-RPC server binary
│   │   ├── Cargo.toml
│   │   └── src/main.rs
│   ├── hero_recipes_client/            # Cross-platform client SDK
│   │   └── Cargo.toml
│   ├── hero_recipes_rhai/              # Rhai scripting bindings
│   │   ├── Cargo.toml
│   │   └── src/lib.rs
│   └── hero_recipes_admin/            # Admin panel (Unix socket)
│       ├── Cargo.toml
│       └── src/main.rs

3. Build and run

Use Nu shell to build and manage generated services:

cargo build -p hero_recipes                   # Generates types + handlers
service recipes install                        # Build + install binaries
service recipes start                          # Start via hero_proc
service recipes start --update                 # Rebuild, reinstall, restart
service recipes start --update --reset         # Rebuild + reset data
service recipes stop                           # Stop via hero_proc
service recipes status                         # Check status

The server exposes a single unified Unix socket:

$HERO_SOCKET_DIR/hero-recipes/rpc.sock    (default: ~/hero/var/sockets/hero-recipes/rpc.sock)

All domains, management methods, and discovery are served through this one socket:

  • POST /rpc — JSON-RPC 2.0 API with CRUD operations per root object
  • GET /health — Health check
  • GET /openrpc.json — OpenRPC specification
  • GET /.well-known/heroservice.json — Discovery manifest

Context isolation is handled via the X-Hero-Context header (integer, default 0 = admin).

4. Use the client

Generated client code provides typed access:

use hero_recipes_client::RecipeClient;

let client = RecipeClient::new("http://localhost:8080", "root", "recipes")?;
let recipes = client.recipe_list().await?;

Package Details

hero_rpc_osis

The core framework. Handles:

  • OSchema parsing — Reads .oschema files and produces an AST
  • Code generation — Generates Rust types, server handlers, client SDKs, and Rhai bindings via build.rs
  • SmartID (SID) — 4-6 character base-36 distributed identifiers for all objects
  • Filesystem persistence — OTOML-format storage with directory-based organization (DBTyped<T>)
  • SmartID — short, collision-free base-36 IDs, shared with hero_lib (herolib_oschema::types::SmartId)
  • Optional full-text _find — the index feature wires @index fields to the hero_indexer service
  • Workspace scaffoldinglab blueprint --schemas-dir <dir> drives WorkspaceScaffolder from hero_rpc_generator to emit a complete service workspace

Feature flags on hero_rpc_osis:

  • index — async full-text _find via hero_indexer_sdk (pulls tokio + tracing)
  • rhai — Rhai scripting bindings for the local storage API
  • test-support — tempfile-backed ephemeral helpers

The RPC server runtime is no longer in this repo. Serving an OSIS-backed service goes through hero_lib: herolib_derive::openrpc_server! generates the trait + dispatch + serve_domains launcher, the generated impl …Api delegates to DBTyped, and hero_lifecycle runs it.

Generated client (from hero_lib)

The typed client is generated by herolib_derive::openrpc_client! over the herolib_openrpc transport (UDS-HTTP). The generated <Svc>Client owns its transport and exposes a method per RPC method:

let client = MyServiceClient::connect_socket("my_service/rpc.sock").await?;
let entries = client.entry_list(Default::default()).await?;

hero_rpc_client (this repo) remains as a lightweight cross-platform (native + WASM) client used by generated SDKs where a hand-rolled client is needed; its native transport is herolib_openrpc::OpenRpcTransport.

Architecture

.oschema files
    │
    ├──→ hero_rpc_generator ──→ scaffolds the service workspace:
    │         (WorkspaceScaffolder)   server + admin + web + sdk crates
    │
    └──→ at build/serve time, hero_lib macros do the rest:
              herolib_derive::openrpc_server!  → wire types + trait + dispatch + serve_domains
              herolib_derive::openrpc_client!  → typed client over herolib_openrpc
              hero_lifecycle                   → socket bind, --info, lifecycle
              hero_rpc_osis (this repo)        → DBTyped storage behind the trait impl

Code generation flow:

  1. build.rs in the core crate calls OschemaBuilder::generate()
  2. OSchema files are parsed into an AST
  3. Rust types with #[derive(OsisObject)] are generated for each root object
  4. Server handlers (CRUD + custom methods) are generated for the _server crate
  5. Client methods are generated for the _client crate
  6. OpenRPC specification JSON is emitted alongside

Runtime flow:

  1. _server binary starts, creates a single Unix socket per service
  2. Registers domain handlers with the unified Axum JSON-RPC router
  3. Context isolation via X-Hero-Context header (integer: 0 = admin, ≥1 = user)
  4. hero_router maps external TCP to service sockets (services never open TCP ports)
  5. _client SDK connects via HTTP and sends JSON-RPC 2.0 requests

Building

cargo check --workspace
cargo build --workspace

Note: This repo uses Nu shell scripts for all build and lifecycle management. Makefiles and bash scripts are not used. Do not add them.

Requirements

  • Rust 1.93.0+
  • Edition 2024
  • Nu shell for service lifecycle management
  • hero_lib — Core Rust libraries (otoml, derive, sid, infra)
  • hero_skills — Hero service conventions (hero_sockets, hero_context, hero_proc_sdk)
  • hero_proc — Process supervisor and lifecycle manager

Architecture Decision Records

License

Apache-2.0