Skip to content

Engine

The KykuEngine is the core orchestrator that drives the entire infrastructure lifecycle. It coordinates the dependency graph, diff engine, planner, state manager, crypto, and provider calls.

Config → Inject Auto-Subnets → Build DAG → Load State → Query Cloud → Diff → Plan → Apply → Persist State
const engine = new KykuEngine({ stateDir: './.kyku' });
const plan = await engine.plan(config, { provider: awsProvider });

The config is a TypeScript module that exports resource instances. The engine receives an array of BaseResource objects.

Before building the graph, the engine scans for Vpc resources with distributeAcrossAzs > 0 and injects synthetic Subnet resources:

// Vpc with distributeAcrossAzs: 2
// Injects: vpc-main-subnet-public-0, vpc-main-subnet-public-1
// vpc-main-subnet-private-0, vpc-main-subnet-private-1

These subnets appear in the dependency graph, state file, and outputs like any other resource.

The GraphBuilder scans every resource property for BaseResource instances. A reference like vm.network = myVpc creates a Vm → Vpc edge. The graph assigns levels via topological sort.

Reads .kyku/state.<env>.json for last known cloud provider IDs and configs. If no state file exists, all resources are considered new.

For resources with a providerId in state, calls provider.readState(resource, providerId) to detect drift. Resources without a providerId skip this step.

The DiffEngine compares desired config (from resourceToConfig()) vs actual config (from readState). Changes are categorized:

Change Type Description
create Resource exists in config but not in state
update Config differs from cloud state
replace Immutable property changed (destroy + recreate)
destroy Resource exists in state but not in config
no-op Config matches cloud state

The planner produces an ordered list of changes grouped by graph level. Resources at the same level run in parallel.

Executes changes in dependency order, reporting progress in real-time. Each change calls the appropriate provider method:

  • createprovider.createResource(resource)
  • updateprovider.updateResource(resource)
  • replaceprovider.destroyResource() + provider.createResource()
  • destroyprovider.destroyResource()

After applying, the new provider IDs, configs, and outputs are written to the state file. A timestamped backup of the previous state is saved before every apply.

class KykuEngine {
constructor(options: EngineOptions);
// Lifecycle methods
plan(config: ResourceConfig[], options: PlanOptions): Promise<Plan>;
apply(config: ResourceConfig[], options: ApplyOptions, plan?: Plan): Promise<ApplyResult>;
destroy(options: DestroyOptions): Promise<DestroyResult>;
refresh(options: RefreshOptions): Promise<RefreshResult>;
// State access
readState(env?: string): Promise<StateFile | null>;
getOutputs(options: OutputOptions): Promise<Record<string, any>>;
// Graph
buildGraph(config: ResourceConfig[]): Graph;
}
Option Type Default Description
stateDir string ./.kyku Directory for local state files
backend StateBackendConfig Optional remote backend (s3, gcs, spaces, or http)
deployPrefix string random 8-char hex Prefix for resource names
Option Type Default Description
provider Provider required Provider implementation
env string '' Environment workspace
Option Type Default Description
provider Provider required Provider implementation
autoApprove boolean false Skip confirmation prompt
parallelism number 10 Max concurrent operations
env string '' Environment workspace

Every deployment gets a unique 8-char hex prefix, auto-generated on first plan/apply and stored in StateFile.metadata.prefix. Resource names are wrapped with a deep Proxy that prepends prefix- to name for new resources while keeping existing resource names unchanged.

Local backend: file lock at {stateDir}/.kyku.lock with PID + timestamp. S3 backend: lock object at ${key}.lock in the configured bucket. Locks are auto-released on success/error via try/finally. A 60s heartbeat refreshes the timestamp; stale locks (>5 minutes) are broken only when a re-read still shows the same token. Default 30s timeout for acquiring lock.

Phase Behavior
Validation Config errors surface immediately with file/line info
Plan Cloud API errors during discovery abort the plan
Apply On failure, partial state is preserved. Resources that succeeded remain applied.
Destroy Retry with backoff (5 attempts, 2^n × 2s). Verify deletion with poll (10 attempts, 2s).

Kyku uses ANSI cursor control for in-place terminal updates:

  • \x1b[1A (cursor up) + \x1b[2K (clear line) for redraw
  • Tracks exact linesPrinted count
  • Spinner animation at ~80ms via setInterval
  • No leading/trailing newlines in render to avoid scroll accumulation