HeroProc SDK Architecture Update #38

Closed
opened 2026-04-05 08:14:42 +00:00 by despiegk · 0 comments
Owner

HeroProc SDK Architecture Update

1. Libraries

hero_proc_sdk

Core layer (OpenRPC-based)

  • Generated OpenRPC client

  • Transport (UDS + HTTP)

  • Thin typed wrappers:

    • services()
    • runs()
    • jobs()
    • logs()
    • secrets()

⚠️ No builders here
⚠️ No UX abstractions
👉 Pure, correct API layer


hero_proc_factory

User-facing layer (what people actually use)

  • Connection factory
  • Builder pattern APIs
  • High-level workflows

👉 This is the default entrypoint for developers


2. Entry Point (Factory)

Goal

One simple way to connect, regardless of transport

let proc = ProcoFactory::local_uds("/var/run/heroproc.sock")?;
let proc = ProcoFactory::http("http://127.0.0.1:8000")?;

Returns:

HeroProcClient

3. HeroProcClient

Wraps hero_proc_sdk internally.

Domain access (thin pass-through)

proc.services()
proc.runs()
proc.jobs()
proc.logs()
proc.secrets()

Builder entrypoints

proc.service_builder("myapp")
proc.run_builder("adhoc-task")

4. Service Builder

Purpose

Create + update services (ServiceConfig → Actions)

Example

proc.service_builder("myapp")
    .description("Web service")
    .action("prepare", |a| {
        a.script("python setup.py")
         .cwd("/srv/myapp")
    })
    .action("start", |a| {
        a.script("python app.py")
         .depends_on(["prepare"])
         .restart_always()
    })
    .save()?;

Responsibilities

  • Build ServiceConfig

  • Wrap ActionSpec

  • Validate:

    • dependency graph
    • duplicate action names
  • Call:

proc.services().put(config)

5. Run Builder (Ad-hoc execution)

Purpose

Execute actions without a service

Example

let run = proc
    .run_builder("build-run")
    .action("build", |a| {
        a.script("cargo build --release")
    })
    .action("test", |a| {
        a.script("cargo test")
         .depends_on(["build"])
    })
    .start()?;

Responsibilities

  • Build Run + Jobs
  • Attach ActionSpecs inline
  • Call:
proc.runs().start(run)

6. Action Builder (Core abstraction)

Used in both ServiceBuilder and RunBuilder.

Example

.action("build", |a| {
    a.command("cargo build")
     .cwd("/repo")
     .env("RUST_LOG", "info")
     .timeout_secs(600)
     .retry(3)
})

Must support

  • script(...) or command(...)
  • cwd(...)
  • env(k, v)
  • timeout_secs(...)
  • depends_on([...])
  • retry(n)
  • restart_always() (service mode)

7. Service Lifecycle Helpers

Start service → creates Run

proc.services().start("myapp")?;

Equivalent to:

  • create Run
  • attach service_id
  • execute actions

Restart service

proc.services().restart("myapp")?;

Should:

  • delete old jobs (default behavior)
  • create new Run

8. Run Helpers

proc.runs().wait(run_id)?;
proc.runs().status(run_id)?;
proc.runs().logs(run_id)?;

9. Design Principles (Enforced)

  • Run is universal abstraction
  • Service = reusable Run template
  • Builder = hides ActionSpec complexity
  • Factory = hides transport complexity
  • SDK = stays minimal + correct

10. Golden UX (What success looks like)

Service

let proc = ProcoFactory::local_uds(...)?

proc.service_builder("api")
    .action("start", |a| {
        a.script("bun run server.ts")
    })
    .save()?;

proc.services().start("api")?;

Ad-hoc run

proc.run_builder("one-shot")
    .action("task", |a| {
        a.script("python job.py")
    })
    .start()?;
# HeroProc SDK Architecture Update ## 1. Libraries ### `hero_proc_sdk` Core layer (OpenRPC-based) * Generated OpenRPC client * Transport (UDS + HTTP) * Thin typed wrappers: * `services()` * `runs()` * `jobs()` * `logs()` * `secrets()` ⚠️ No builders here ⚠️ No UX abstractions 👉 Pure, correct API layer --- ### `hero_proc_factory` User-facing layer (what people actually use) * Connection factory * Builder pattern APIs * High-level workflows 👉 This is the default entrypoint for developers --- # 2. Entry Point (Factory) ## Goal One simple way to connect, regardless of transport ```rust let proc = ProcoFactory::local_uds("/var/run/heroproc.sock")?; let proc = ProcoFactory::http("http://127.0.0.1:8000")?; ``` Returns: ```rust HeroProcClient ``` --- # 3. HeroProcClient Wraps `hero_proc_sdk` internally. ## Domain access (thin pass-through) ```rust proc.services() proc.runs() proc.jobs() proc.logs() proc.secrets() ``` --- ## Builder entrypoints ```rust proc.service_builder("myapp") proc.run_builder("adhoc-task") ``` --- # 4. Service Builder ## Purpose Create + update services (ServiceConfig → Actions) ## Example ```rust proc.service_builder("myapp") .description("Web service") .action("prepare", |a| { a.script("python setup.py") .cwd("/srv/myapp") }) .action("start", |a| { a.script("python app.py") .depends_on(["prepare"]) .restart_always() }) .save()?; ``` ## Responsibilities * Build `ServiceConfig` * Wrap `ActionSpec` * Validate: * dependency graph * duplicate action names * Call: ```rust proc.services().put(config) ``` --- # 5. Run Builder (Ad-hoc execution) ## Purpose Execute actions without a service ## Example ```rust let run = proc .run_builder("build-run") .action("build", |a| { a.script("cargo build --release") }) .action("test", |a| { a.script("cargo test") .depends_on(["build"]) }) .start()?; ``` ## Responsibilities * Build Run + Jobs * Attach ActionSpecs inline * Call: ```rust proc.runs().start(run) ``` --- # 6. Action Builder (Core abstraction) Used in both ServiceBuilder and RunBuilder. ## Example ```rust .action("build", |a| { a.command("cargo build") .cwd("/repo") .env("RUST_LOG", "info") .timeout_secs(600) .retry(3) }) ``` ## Must support * `script(...)` or `command(...)` * `cwd(...)` * `env(k, v)` * `timeout_secs(...)` * `depends_on([...])` * `retry(n)` * `restart_always()` (service mode) --- # 7. Service Lifecycle Helpers ## Start service → creates Run ```rust proc.services().start("myapp")?; ``` Equivalent to: * create Run * attach service_id * execute actions --- ## Restart service ```rust proc.services().restart("myapp")?; ``` Should: * delete old jobs (default behavior) * create new Run --- # 8. Run Helpers ```rust proc.runs().wait(run_id)?; proc.runs().status(run_id)?; proc.runs().logs(run_id)?; ``` --- # 9. Design Principles (Enforced) * **Run is universal abstraction** * **Service = reusable Run template** * **Builder = hides ActionSpec complexity** * **Factory = hides transport complexity** * **SDK = stays minimal + correct** --- # 10. Golden UX (What success looks like) ## Service ```rust let proc = ProcoFactory::local_uds(...)? proc.service_builder("api") .action("start", |a| { a.script("bun run server.ts") }) .save()?; proc.services().start("api")?; ``` --- ## Ad-hoc run ```rust proc.run_builder("one-shot") .action("task", |a| { a.script("python job.py") }) .start()?; ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_proc#38
No description provided.