AbsoluteJS

Autoscaler

@absolutejs/autoscalerv0.2.1betaPlatform & Infra

Policy-driven horizontal autoscaling loop for Bun fleets — pluggable signals, declarative thresholds, and an actuator you supply.

Runs the decision loop of a horizontal autoscaler for infrastructure you operate yourself: it reads pluggable signals (CPU, queue depth, p95 latency), combines them into a pressure score, and asks your actuator to spawn or drain instances within declarative min/max, threshold, and cooldown rules. Because the actuator is caller-supplied, the same loop scales VMs, containers, processes, or isolates — you define what an instance is. It is the same scaling engine that powers AbsoluteJS hosting, packaged for any Bun fleet you run on your own servers.

#Installation

BASH
bun add @absolutejs/autoscaler

#Capabilities

Overview

Horizontal-scaling policy used by the hosted AbsoluteJS.ai platform and available to any Bun control plane.

@absolutejs/autoscaler owns the decision half of an autoscaler. The actuator half — actually provisioning a VM, draining a pod, killing a process — lives in your control plane via an injected Actuator, so the substrate stays cloud-agnostic and runtime-agnostic.

The same substrate fits:

Show 4 more

a 10-VM fleet across DigitalOcean / Hetzner / Linode

a 10000-isolate fleet on one box via @absolutejs/isolated-jsc

a pod-per-tenant cluster on Kubernetes

The actuator defines what "instance" means.

Signals

A Signal is { name, read: () => SignalReading | Promise<SignalReading> }.

A SignalReading is { score, observed? }. score is normalized: 1.0 = "at the scale-up target." Going past 1.0 represents over- target pressure.

The bundled ratioSignal(name, target, read) helper builds the canonical "observed / target" shape for CPU utilization, memory pressure, queue depth, latency, etc.

Show 1 more

A signal that throws is captured as { failed: true, error } in the decision readings and excluded from the combined score. The loop never breaks because one metric source is offline.

Combine strategies

Combine readings with 'max' (worst pressure wins — the safe default for elasticity), weighted 'avg', or a custom function over the raw readings.

Cooldowns

scaleUp and scaleDown have independent cooldown timers — a fleet that just scaled up can still scale down moments later if the load drops. Default cooldownMs is 60 seconds.

Audit trail

When an audit broker is supplied, every applied decision emits:

autoscaler.scale.up — with { score, currentCount, desiredCount, reason }

autoscaler.scale.down

Show 2 more

autoscaler.hold

All three are emitted (even hold) so the audit trail tells the full story of why the fleet is where it is. Broker failures are isolated; they bump the metrics().errors counter but never break the loop.

Declarative scaling policy

createPolicy sets min/max instance counts, scale-up and scale-down thresholds, step sizes, and independent cooldown timers — a fleet that just scaled up can still scale down moments later.

Pluggable signals

The ratioSignal helper normalizes any observed/target metric (CPU utilization, queue depth, latency) to a common score. A signal that throws is excluded from the score, so one offline metric source never breaks the loop.

Bring your own actuator

You supply list, spawn, drain, and terminate callbacks. The decision logic stays cloud-agnostic while your control plane talks to Hetzner, Kubernetes, or a local process pool.

Built-in audit trail

With an optional audit broker attached, every decision emits autoscaler.scale.up, autoscaler.scale.down, or autoscaler.hold events, so the trail explains why the fleet is where it is.

#Scaling decision loop

Every evaluation turns provider-neutral signals into an explicit, bounded scaling decision.

  1. Observe
    Read CPU, latency, queue depth, or a custom signal.
  2. Score
    Combine and normalize signals into the configured score.
  3. Decide
    Apply thresholds, minimums, maximums, and cooldowns.
  4. Act
    Call the replaceable infrastructure actuator.
  5. Measure
    Emit the decision and evidence for audit and tuning.

Outcomes

What you can build

Overview

Horizontal-scaling policy used by the hosted AbsoluteJS.ai platform and available to any Bun control plane.

Signals

A Signal is { name, read: () => SignalReading | Promise<SignalReading> }.

Combine strategies

'max' (default) — worst pressure wins. The safe choice for

Hardening checklist

Production guidance

Make every external boundary explicitPin the deployed @absolutejs/autoscaler version, replace example or memory-backed dependencies with durable implementations, bound external calls, protect credentials, and emit enough evidence to retry or recover safely.

Follow in order

Troubleshooting path

1
Trace from the first failed boundary
Reproduce the smallest canonical @absolutejs/autoscaler example, confirm the supported entry point and version in the API explorer, then inspect the first boundary that did not produce its documented result.

#Loop

Partial snippet

Working example for Loop.

TXT
read signals  →  combine into a score  →  compare to thresholds  →
if past threshold & cooldown elapsed  →  ask actuator to spawn/drain
                                          (clamped to min/max)

#API

Partial snippet

Working example for API.

TS
import {
  createAutoscaler,
  createPolicy,
  ratioSignal,
} from "@absolutejs/autoscaler";

const scaler = createAutoscaler({
  policy: createPolicy({
    min: 1,
    max: 20,
    scaleUp: { threshold: 0.75, cooldownMs: 60_000, step: 1 },
    scaleDown: { threshold: 0.3, cooldownMs: 300_000, step: 1 },
  }),
  signals: [
    ratioSignal("cpu", 0.8, async () => await meter.cpuUtilization()),
    ratioSignal("queue", 100, async () => await queue.depth(), {
      observedKey: "depth",
    }),
    ratioSignal("latencyP95", 200, async () => await metrics.p95()),
  ],
  combine: "max", // worst pressure wins. or 'avg', or a custom fn
  actuator: {
    list: () => fleet.list(),
    spawn: () => fleet.provision(),
    drain: (id) => loadBalancer.remove(id),
    terminate: (id) => fleet.destroy(id),
  },
  audit: broker, // optional; emits autoscaler.scale.up etc.
  intervalMs: 30_000,
});

scaler.start();
// fires every 30s

const reviewedPlan = await scaler.evaluate();
await scaler.applyDecision(reviewedPlan, { maxAgeMs: 300_000 });
// applies that exact plan only while its capacity precondition still holds

const oneShot = await scaler.step();
// { action: 'scale-up' | 'scale-down' | 'hold', score, currentCount,
//   desiredCount, reason, readings: [...], at }

#Quick Start

Partial snippet

Wire signals, a policy, and your actuator into a running loop. The actuator callbacks define what an instance is — here a VM fleet behind a load balancer.

TS
import {
  createAutoscaler,
  createPolicy,
  ratioSignal,
} from '@absolutejs/autoscaler';

const scaler = createAutoscaler({
  policy: createPolicy({
    min: 1,
    max: 20,
    scaleUp:   { threshold: 0.75, cooldownMs: 60_000, step: 1 },
    scaleDown: { threshold: 0.30, cooldownMs: 300_000, step: 1 },
  }),
  signals: [
    ratioSignal('cpu',   0.80, async () => await meter.cpuUtilization()),
    ratioSignal('queue', 100,  async () => await queue.depth(),
                               { observedKey: 'depth' }),
    ratioSignal('latencyP95', 200, async () => await metrics.p95()),
  ],
  combine: 'max', // worst pressure wins. or 'avg', or a custom fn
  actuator: {
    list:      () => fleet.list(),
    spawn:     () => fleet.provision(),
    drain:     (id) => loadBalancer.remove(id),
    terminate: (id) => fleet.destroy(id),
  },
  intervalMs: 30_000,
});

scaler.start();
// evaluates signals and applies a decision every 30s

#One-Shot Decisions

Partial snippet

Call step() for a single evaluation instead of the interval loop — useful for cron-driven scaling or testing a policy against live signals.

TS
const decision = await scaler.step();
// {
//   action: 'scale-up' | 'scale-down' | 'hold',
//   score, currentCount, desiredCount,
//   reason, readings: [...], at
// }
Decisions, not provisioning
This package owns the decision half of an autoscaler. The actuator half — actually provisioning a VM, draining a pod, killing a process — is the callback set you inject, so nothing here is tied to a specific cloud.
Beta
The package is pre-1.0. The policy and signal shapes are settling, but expect minor API adjustments before a stable release.

#API reference

Search the declarations exported by the current package type files. Expand a symbol to inspect its source-backed signature.

17 symbols
AutoscalerAuditLiketypePermalink

@absolutejs/autoscaler — horizontal-scaling policy substrate. The package contributes the decision half of a PaaS autoscaler. The actuator half — actually provisioning a VM / draining a pod / killing a process — lives in the control plane via the Actuator interface, so the substrate stays cloud- agnostic. Loop: read signals → combine into a single score (0..1+) → compare against scaleUp / scaleDown thresholds → if past threshold and cooldown elapsed, ask actuator to spawn N / drain N / terminate

TS
type AutoscalerAuditLike = {
    append: (event: {
        kind: string;
        actor?: string;
        target?: string;
        metadata?: Record<string, unknown>;
    }) => Promise<void> | void;
};
Exported from @absolutejs/autoscaler

Current package surface

What ships today

@absolutejs/autoscalerv0.2.1 · betaPlatform & InfranpmSource
1entry points17symbols

Import surface · click to copy