Files
horus/docs/job-format.md
2025-11-14 11:10:32 +01:00

3.7 KiB

Job Format

Jobs are the fundamental unit of work in Horus.

Structure

pub struct Job {
    pub id: String,              // Unique job identifier
    pub runner_id: String,       // Target runner ID
    pub payload: String,         // Job payload (script/command)
    pub timeout: Option<u64>,    // Timeout in seconds
    pub env_vars: HashMap<String, String>, // Environment variables
    pub signatures: Vec<Signature>, // Cryptographic signatures
    pub created_at: i64,         // Creation timestamp
    pub status: JobStatus,       // Current status
}

Job Status

pub enum JobStatus {
    Pending,    // Queued, not yet started
    Running,    // Currently executing
    Completed,  // Finished successfully
    Failed,     // Execution failed
    Timeout,    // Exceeded timeout
    Cancelled,  // Manually cancelled
}

Signature Format

pub struct Signature {
    pub public_key: String,  // Signer's public key
    pub signature: String,   // Cryptographic signature
    pub algorithm: String,   // Signature algorithm (e.g., "ed25519")
}

Creating a Job

Minimal Job

use hero_job::Job;

let job = Job::new(
    "my-runner",
    "print('Hello World')".to_string(),
);

With Timeout

let job = Job::builder()
    .runner_id("my-runner")
    .payload("long_running_task()")
    .timeout(300) // 5 minutes
    .build();

With Environment Variables

use std::collections::HashMap;

let mut env_vars = HashMap::new();
env_vars.insert("API_KEY".to_string(), "secret".to_string());
env_vars.insert("ENV".to_string(), "production".to_string());

let job = Job::builder()
    .runner_id("my-runner")
    .payload("deploy_app()")
    .env_vars(env_vars)
    .build();

With Signature

use hero_job::{Job, Signature};

let job = Job::builder()
    .runner_id("my-runner")
    .payload("important_task()")
    .signature(Signature {
        public_key: "ed25519:abc123...".to_string(),
        signature: "sig:xyz789...".to_string(),
        algorithm: "ed25519".to_string(),
    })
    .build();

Payload Format

The payload format depends on the target runner:

Hero Runner

Heroscript content:

!!git.list
print("Repositories listed")
!!docker.ps

SAL Runner

Rhai script with SAL modules:

let files = os.list_dir("/tmp");
for file in files {
    print(file);
}

Osiris Runner

Rhai script with Osiris database:

let users = osiris.model("users");
let user = users.create(#{
    name: "Alice",
    email: "alice@example.com"
});

Job Result

pub struct JobResult {
    pub job_id: String,
    pub status: JobStatus,
    pub output: String,      // Stdout
    pub error: Option<String>, // Stderr or error message
    pub exit_code: Option<i32>,
    pub started_at: Option<i64>,
    pub completed_at: Option<i64>,
}

Best Practices

Timeouts

  • Always set timeouts for jobs
  • Default: 60 seconds
  • Long-running jobs: Set appropriate timeout
  • Infinite jobs: Use separate monitoring

Environment Variables

  • Don't store secrets in env vars in production
  • Use vault/secret management instead
  • Keep env vars minimal
  • Document required variables

Signatures

  • Always sign jobs in production
  • Use strong algorithms (ed25519)
  • Rotate keys regularly
  • Store private keys securely

Payloads

  • Keep payloads concise
  • Validate input data
  • Handle errors gracefully
  • Log important operations

Validation

Jobs are validated before execution:

  1. Structure: All required fields present
  2. Signature: Valid cryptographic signature
  3. Runner: Target runner exists and available
  4. Payload: Non-empty payload
  5. Timeout: Reasonable timeout value

Invalid jobs are rejected before execution.