Ghidora Docs

Ghidora⚡

Zero-dependency monorepo orchestrator.

Run tasks across Node, Bun, Deno, and more with a Rust/WASM core.

       ⠀⠀⣠⣤⣶⣶⡞⡀⣤⣬⣴⠀⠀⢳⣶⣶⣤⣄⡀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⡇⠀⢸⣿⠿⣿⡇⠀⠀⠸⣿⣿⣿⣿⣷⣦⡀⠀⠀⠀
⠀⠀⢠⡾⣫⣿⣻⣿⣽⣿⡇⠀⠈⢿⣧⡝⠟⠀⠀⢸⣿⣿⣿⣿⣿⣟⢷⣄⠀⠀
⠀⢠⣯⡾⢿⣿⣿⡿⣿⣿⣿⣆⣠⣶⣿⣿⣷⣄⣰⣿⣿⣿⣿⣿⣿⣿⢷⣽⣄⠀
⢠⣿⢋⠴⠋⣽⠋⡸⢱⣯⡿⣿⠏⣡⣿⣽⡏⠹⣿⣿⣿⡎⢣⠙⢿⡙⠳⡙⢿⠄
⣰⢣⣃⠀⠊⠀⠀⠁⠘⠏⠁⠁⠸⣶⣿⡿⢿⡄⠈⠀⠁⠃⠈⠂⠀⠑⠠⣈⡈⣧
⡏⡘⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡥⢄⢸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢳⢸
⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣄⣸⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢨
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀
          __    _     __
   ____ _/ /_  (_)___/ /___  _________ _
  / __ `/ __ \/ / __  / __ \/ ___/ __ `/
 / /_/ / / / / / /_/ / /_/ / /  / /_/ /
 \__, /_/ /_/_/\__,_/\____/_/   \__,_/
/____/

Why Ghidora exists

Ghidora was built with a simple goal: remove the friction from managing large, multi-package codebases. Modern monorepos often rely on layers of plugins, heavy dependency trees, and opaque build logic. While powerful, these systems can become difficult to reason about, slow to start, and hard to debug. Ghidora takes a different approach — it focuses on clarity, determinism, and raw performance.

At its core, Ghidora is a zero-dependency orchestrator. It does not rely on external JavaScript ecosystems, plugin chains, or hidden runtime layers. Instead, it provides a predictable execution model where every task, dependency, and cache artifact is explicitly defined. This makes builds easier to understand and eliminates surprises during execution.

Built for simplicity and control

The design philosophy is minimal but powerful. Ghidora gives developers full control over how tasks are defined and executed, without forcing conventions or abstractions that get in the way. Whether you're running builds, tests, or custom scripts across hundreds of packages, the system stays transparent and easy to follow.

Parallel execution and deterministic caching are first-class features, not afterthoughts. This means faster feedback loops and consistent results across environments, from local development to CI pipelines.

Runs everywhere — including Alpine Linux — built with Rust and WebAssembly

The goal is not to compete with existing tools through complexity, but to provide a clean, fast, and dependable foundation that developers can trust and extend. Ghidora is built to stay small, predictable, and focused — so you can spend less time configuring tooling and more time building software.

Installation

Install via NPM

Default Node.js package manager. Works everywhere.

npm install @hyperforge/ghidora -g

Install via PNPM

Fast and disk-efficient. Best for monorepos (Recommended).

pnpm install @hyperforge/ghidora -g

Install via YARN

Stable alternative to npm with good caching.

yarn install @hyperforge/ghidora -g

Install via BUN

Ultra-fast runtime and package manager (Fastest).

bun install @hyperforge/ghidora -g

Install via DENO

Secure runtime with permission-based execution.

deno install -g npm:@hyperforge/ghidora

Quick Start

Ghidora runs tasks across your monorepo, resolves package dependencies, and supports local or cloud cache when configured.

Setup commands

Initialize the workspace

Creates the root package.json, ghidora.config.mjs, and the standard apps/ and packages/ folders when they do not already exist.

ghidora init

Create an app

Creates apps/dashboard with a starter package.json, esbuild.config.js, and src/main.ts.

ghidora init --app dashboard

Create a library

Creates packages/ui with a starter package.json, tsconfig.json, and src/index.ts.

ghidora init --lib ui

Common commands

Run a workspace task

Runs a task across all matched packages. If dependencies are configured for that task, Ghidora runs them first in the correct order.

ghidora build

Run one package task

Runs a single task in one package. Use this when you want to target a specific package instead of the full workspace.

ghidora run app:web

Run several explicit targets

Runs the listed targets as separate jobs while still honoring the global concurrency limit.

ghidora run-many app:web packages:core:test

Run targets with dependencies

Runs each target and also walks its declared task dependencies before execution.

ghidora run-many-deps app:web packages:core:test

Execute a raw command

Runs the same shell command inside every selected package. Useful for commands that are not package scripts.

ghidora exec "npm test"

Inspect the graph

Starts a local HTML view of the workspace graph so you can inspect package relationships and task dependencies.

ghidora graph

List discovered packages

Prints all packages discovered from the workspace patterns in ghidora.config.mjs.

ghidora ls

Execution flags

Scope to specific packages

Limits execution to the packages you name. This is the main way to narrow a workspace-wide run.

ghidora build --scope app-web,packages-core

Skip cache completely

Disables both cache restore and cache save. Use this when you want a clean run every time.

ghidora build --no-cache

Preview only

Prints the execution plan without running tasks. Good for checking order and dependency flow.

ghidora build --dry-run

Keep going after failures

Continues running other tasks even if one task fails. Helpful when debugging multiple broken packages.

ghidora build --no-bail

Affected support

This flag is parsed in the CLI, but it is not currently wired into execution. Treat it as reserved for future affected-package logic.

ghidora build --affected

How the source behaves

ghidora.config.mjs

This is the heart of Ghidora Build-System where you gain full manual control over your monorepo. Ghidora supports multiple languages all want is just define the run/build/test command on respective package's package.json file!!


                        
export default {
  // Root of the monorepo
  workspaceRoot: process.cwd(),
  totalParallelTasks: 3,

  // Package discovery (JS, Rust, Go, Python, anything)
  packages: [
    "packages/*",
    "apps/*"
  ],

  // Local deterministic cache (WASM tar/targz)
  cacheDir: ".ghidora/cache",
  cloudCache: {
    enabled: false,
    url: "http://localhost:4000",
    token: "secret123", // optional
  },
  executor: "npm",
  shell: true,          // opens for raw exec cmds 

  // ONLY tasks declared here get:
  // - DAG ordering
  // - caching
  // - guarantees
  //
  // Undeclared tasks still run (lerna-style),
  // but WITHOUT cache or DAG.
  tasks: {
    build: {
      inputs: [
        "src",
        "Cargo.toml",
        "Cargo.lock",
        "package.json"
      ],
      // Files produced by build (per package)
      // Used ONLY for cache save/restore
      outputs: [
        "dist",
        "build",
        "out",
        "target/release"
      ],

      // Run test on dependency packages first
     // dependsOn: ["test"]
    },

    test: {
      // Tests require build to have run
      dependsOn: ["build"]
    },

    dev: {
      // Long-running (watch / dev servers)
      persistent: true
    },

    serve: {
      // Runtime servers (frontend / backend)
      persistent: true
    }
  }
};

                        

Task dependencies

A task can depend on another task in the same package or on the same task in each local dependency package when prefixed with ^.

dependsOn: ["build", "^build"]

Caching

Ghidora can restore from local cache first, then cloud cache if enabled, and save results back after the task finishes.

cacheDir / cloudCache / --no-cache

Concurrency

Controls the global number of tasks allowed to run at the same time. This is the workspace-wide concurrency cap.

totalParallelTasks

Executor choice

Selects which package manager or runtime should run scripts. The default is npm.

executor: npm | pnpm | yarn | bun | deno

Sandbox and shell

Controls whether commands run through a shell and whether Deno sandbox rules or process-level limits are applied.

enableSandbox / sandbox / shell

Advanced Configuration

Ghidora treats your monorepo as a controlled execution environment — not a loose collection of scripts. Every task runs with explicitly defined permissions, environment, and system identity.

Execution Model

Layered control system

Execution is governed by two independent layers. The OS layer (uid/gid) enforces real process-level isolation. The runtime layer (Deno sandbox) adds strict environment and capability restrictions when enabled.

OS layer + Runtime layer

Runtime behavior differences

Deno provides strict sandboxing and deterministic execution. Node and Bun prioritize ecosystem compatibility — they respect overrides but do not fully isolate the process environment.

Deno vs Node vs Bun

Security & Isolation

Process identity (uid / gid)

All tasks execute under a controlled user and group at the OS level. This is the strongest guarantee in the system and applies consistently across Node, Bun, and Deno.

uid / gid

Runtime sandboxing

When using Deno, tasks execute inside a restricted sandbox with controlled access to filesystem, network, and environment. This is the only mode that provides full runtime isolation.

enableSandbox: true

Environment isolation semantics

Deno: complete environment reset — only declared variables exist. Node/Bun: variables are overridden but the underlying system environment is still present.

clearEnv: true

Executable surface control

PATH becomes an explicit execution surface. In Deno sandbox it behaves as a strict allowlist. In Node/Bun it limits resolution order but does not fully eliminate inherited access.

PATH override

Centralized Environment Control

Single source of truth

All environment variables are defined in one place. This eliminates scattered configuration and ensures deterministic execution across packages.

sandbox.env

Explicit exposure model

Only declared variables are guaranteed to be available. This prevents accidental leakage of secrets and reduces attack surface significantly.

clearEnv + explicit env

Language-agnostic enforcement

Environment and execution rules are applied uniformly regardless of language or tooling.

JS / Rust / Go / Python

ghidora.config.mjs (production-grade)

A minimal but strict configuration: explicit environment, controlled execution identity, and sandboxed runtime where supported.


export default {
  workspaceRoot: process.cwd(),
  totalParallelTasks: 2,

  packages: ["apps/*"],

  cacheDir: ".ghidora/cache",
  cloudCache: {
    enabled: false,
    url: "http://localhost:4000",
  },

  executor: "npm",
  shell: true,

  // =========================
  // SECURITY LAYER
  // =========================

  enableSandbox: true,

  sandbox: {
    cwd: process.cwd(),

    // Environment isolation
    clearEnv: true,

    // OS-level identity (applies to ALL runtimes)
    uid: 1000,
    gid: 1000,

    env: {
      // Custom variables
      POTTA: "POTTA_____________________ENV",

      // Controlled executable surface
      PATH: [
        "/usr/local/bin",
        "/usr/bin",
        "/bin",
        "/home/user/.cargo/bin",
        "/home/user/.deno/bin",
        "/home/user/.bun/bin"
      ].join(":"), // for linux/unix systems
    }
  },

  // =========================
  // TASK SYSTEM
  // =========================

  tasks: {
    build: {
      inputs: [
        "src",
        "Cargo.toml",
        "Cargo.lock",
        "package.json"
      ],
      outputs: [
        "dist",
        "build",
        "out",
        "target/wasm32-unknown-unknown/release/ghidora.wasm",
        "pkg"
      ]
    },

    check: {
      dependsOn: ["build"]
    },

    test: {},

    dev: {
      persistent: true
    },

    serve: {
      persistent: true
    }
  }
};
                        

Core Configuration Controls

Cache location

Controls where Ghidora stores its local cache and graph artifacts. You can relocate this directory to optimize CI, disk usage, or workspace layout.

cacheDir: ".ghidora/cache"

Executor selection

Defines which runtime/package manager executes tasks. This gives full flexibility — Ghidora does not lock you into a single ecosystem.

executor: npm | pnpm | yarn | bun | deno

Shell execution control

When enabled, allows execution of raw shell commands (with pipes, redirects, etc). Execution strictly respects the PATH you define — only binaries from allowed directories are resolved. When disabled, execution is restricted to direct binary invocation only, further reducing attack surface.

shell: true | false

PATH definition (critical)

Defines exactly which binaries are available during execution. This acts as your execution boundary — only listed directories are used for resolving commands.

PATH: [custom list].join('; or :')

PATH separator per OS

Linux / macOS use ':' as PATH separator. Windows uses ';'. Always match the correct separator for your platform when constructing PATH.

: vs ;

Cloud Caching

Ghidora can store and restore build outputs from a remote cache server. This helps speed up CI and distributed teams by avoiding repeated work across machines.

Enable cloud caching

Enable in config

Cloud caching is disabled by default. Set enabled to true and provide your cache server URL.


cloudCache: {
  enabled: true,
  url: "http://localhost:4000",
  token: "secret123"
}
                        

Important behavior

Only non-persistent tasks (like build, test) are cached. Long-running tasks such as dev or serve are never cached.

persistent: false

Server setup

Install dependencies

The cloud cache server is a simple Express app. Install express before running it.

npm install express

Cache server implementation

This server stores and retrieves cache artifacts using simple GET and PUT endpoints.


import express from "express";
import fs from "fs";
import path from "path";

const app = express();

const PORT = process.env.PORT || 4000;
const CACHE_DIR = process.env.CACHE_DIR || ".cloud-cache";
const TOKEN = process.env.CLOUD_TOKEN;

fs.mkdirSync(CACHE_DIR, { recursive: true });

app.use(
  express.raw({
    type: "application/octet-stream",
    limit: "500mb",
  }),
);

app.use((req, res, next) => {
  if (!TOKEN) return next();

  const auth = req.headers.authorization;
  if (auth !== `Bearer ${TOKEN}`) {
    return res.status(401).send("unauthorized");
  }

  next();
});

function getPath(key) {
  const decoded = decodeURIComponent(key);
  const safe = encodeURIComponent(decoded);
  return path.join(CACHE_DIR, safe);
}

app.get("/cache/:key", (req, res) => {
  const filePath = getPath(req.params.key);

  if (!fs.existsSync(filePath)) {
    return res.status(404).end();
  }

  const stream = fs.createReadStream(filePath);

  res.setHeader("content-type", "application/octet-stream");
  stream.pipe(res);

  stream.on("error", () => {
    res.status(500).end();
  });
});

app.put("/cache/:key", (req, res) => {
  const filePath = getPath(req.params.key);

  const stream = fs.createWriteStream(filePath);

  stream.on("error", () => {
    res.status(500).send("write failed");
  });

  stream.on("finish", () => {
    res.status(200).end();
  });

  stream.write(req.body);
  stream.end();
});

app.listen(PORT, () => {
  console.log(`☁️ Cloud cache running on http://localhost:${PORT}`);
});
                        

How it works

Cache flow

Ghidora first checks local cache, then cloud cache. If not found, it runs the task and stores the result.

local → cloud → execute → save

Binary transfer

All cache artifacts are transferred as raw binary to avoid corruption and ensure cross-platform consistency.

application/octet-stream

Authentication

Optional token-based authentication can be enabled to secure your cache server.

Authorization: Bearer <token>