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 -gInstall via PNPM
Fast and disk-efficient. Best for monorepos (Recommended).
pnpm install @hyperforge/ghidora -gInstall via YARN
Stable alternative to npm with good caching.
yarn install @hyperforge/ghidora -gInstall via BUN
Ultra-fast runtime and package manager (Fastest).
bun install @hyperforge/ghidora -gInstall via DENO
Secure runtime with permission-based execution.
deno install -g npm:@hyperforge/ghidoraQuick 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 initCreate an app
Creates apps/dashboard with a starter package.json, esbuild.config.js, and src/main.ts.
ghidora init --app dashboardCreate a library
Creates packages/ui with a starter package.json, tsconfig.json, and src/index.ts.
ghidora init --lib uiCommon 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 buildRun 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:webRun several explicit targets
Runs the listed targets as separate jobs while still honoring the global concurrency limit.
ghidora run-many app:web packages:core:testRun targets with dependencies
Runs each target and also walks its declared task dependencies before execution.
ghidora run-many-deps app:web packages:core:testExecute 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 graphList discovered packages
Prints all packages discovered from the workspace patterns in ghidora.config.mjs.
ghidora lsExecution 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-coreSkip cache completely
Disables both cache restore and cache save. Use this when you want a clean run every time.
ghidora build --no-cachePreview only
Prints the execution plan without running tasks. Good for checking order and dependency flow.
ghidora build --dry-runKeep going after failures
Continues running other tasks even if one task fails. Helpful when debugging multiple broken packages.
ghidora build --no-bailAffected 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 --affectedHow 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-cacheConcurrency
Controls the global number of tasks allowed to run at the same time. This is the workspace-wide concurrency cap.
totalParallelTasksExecutor choice
Selects which package manager or runtime should run scripts. The default is npm.
executor: npm | pnpm | yarn | bun | denoSandbox and shell
Controls whether commands run through a shell and whether Deno sandbox rules or process-level limits are applied.
enableSandbox / sandbox / shellAdvanced 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 layerRuntime 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 BunSecurity & 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 / gidRuntime 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: trueEnvironment isolation semantics
Deno: complete environment reset — only declared variables exist. Node/Bun: variables are overridden but the underlying system environment is still present.
clearEnv: trueExecutable 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 overrideCentralized 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.envExplicit exposure model
Only declared variables are guaranteed to be available. This prevents accidental leakage of secrets and reduces attack surface significantly.
clearEnv + explicit envLanguage-agnostic enforcement
Environment and execution rules are applied uniformly regardless of language or tooling.
JS / Rust / Go / Pythonghidora.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 | denoShell 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 | falsePATH 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: falseServer setup
Install dependencies
The cloud cache server is a simple Express app. Install express before running it.
npm install expressCache 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 → saveBinary transfer
All cache artifacts are transferred as raw binary to avoid corruption and ensure cross-platform consistency.
application/octet-streamAuthentication
Optional token-based authentication can be enabled to secure your cache server.
Authorization: Bearer <token>