QVAC Logo
Usage examplesUtilities

Runtime lifecycle

Suspend and resume the SDK runtime (e.g., when the host app moves to background) and query lifecycle state.

Overview

While running, the SDK keeps live resources in the host process — Hyperswarm sockets, Corestore handles, RAG corestore, registered download streams, and a request gate above handler dispatch. When the host app moves to background (mobile suspend, desktop minimize, daemon hibernation), holding those resources open wastes battery and can break sockets that are torn down by the OS.

suspend() pauses those resources and engages a lifecycle gate so non-lifecycle operations fail fast instead of hanging. resume() restores them when the app returns to foreground. state() is the source of truth for the current runtime state — useful for branching app logic without shadow-tracking suspend/resume locally.

suspend(), resume(), and state() are themselves never blocked by the gate, and all three are idempotent.

Functions

  1. suspend() — call from the background handler
  2. state() — read "active" | "suspending" | "suspended" | "resuming"
  3. resume() — call from the foreground handler

For how to use each function, see SDK — API reference.

Lifecycle states

state() returns one of:

  • "active" — all SDK operations are accepted normally
  • "suspending"suspend() is in progress; non-lifecycle operations are already blocked
  • "suspended" — runtime is paused; only suspend(), resume(), state() are accepted
  • "resuming"resume() is in progress; non-lifecycle operations are still blocked

A partial resume() failure leaves the runtime in "suspended" (not "active"), so callers can retry resume() without leaking the failed state.

Behavior while suspended

While runtime state is not "active", only suspend(), resume(), and state() are accepted. Any other request fails fast with LIFECYCLE_OPERATION_BLOCKED instead of hanging.

In-flight operations started before suspend() follow the matrix below:

OperationDuring suspendAfter resume
P2P / Hyperdrive downloadStalls cleanlyContinues automatically
HTTP downloadBypass — bytes keep flowing(Already flowing)
Local native inference (e.g. completion())Runs to completionn/a
Delegated reply RPCStallsAuto-recovers (subject to delegate timeout)
Delegated stream RPCSevered; consumer iterator hangs silentlyNot recovered — re-issue after resume()
New operation (e.g. completion(), loadModel())Throws LIFECYCLE_OPERATION_BLOCKEDAccepted

Example

The following script loads a model, runs a completion, suspends the runtime, demonstrates that a new completion() is blocked while suspended, then resumes and runs another completion. state() is sampled at each step:

runtime-lifecycle.js
import { loadModel, LLAMA_3_2_1B_INST_Q4_0, completion, unloadModel, suspend, resume, state, } from "@qvac/sdk";
try {
    // Load a model
    const modelId = await loadModel({
        modelSrc: LLAMA_3_2_1B_INST_Q4_0,
        modelType: "llm",
        onProgress: (progress) => {
            console.log(progress);
        },
    });
    console.log("✅ Model loaded\n");
    console.log(`📊 Lifecycle state: ${await state()}\n`);
    // Run a completion before suspending
    console.log("--- Completion before suspend ---");
    const result1 = completion({
        modelId,
        history: [{ role: "user", content: "Say hello in one word" }],
        stream: true,
    });
    for await (const token of result1.tokenStream) {
        process.stdout.write(token);
    }
    console.log("\n");
    // Suspend all networking and storage (e.g. app going to background)
    console.log("⏸️  Suspending...");
    await suspend();
    console.log(`📊 Lifecycle state: ${await state()}\n`);
    try {
        await completion({
            modelId,
            history: [{ role: "user", content: "This should fail" }],
            stream: false,
        }).text;
    }
    catch (error) {
        const name = error.name;
        if (name === "LIFECYCLE_OPERATION_BLOCKED") {
            console.log(`🚫 Operation blocked while suspended (${name})`);
        }
        else {
            throw error;
        }
    }
    // Simulate time in background
    console.log("\n💤 Simulating 3 seconds in background...");
    await new Promise((resolve) => setTimeout(resolve, 3000));
    // Resume when returning to foreground
    console.log("▶️  Resuming...");
    await resume();
    console.log(`📊 Lifecycle state: ${await state()}\n`);
    // Run another completion after resuming
    console.log("--- Completion after resume ---");
    const result2 = completion({
        modelId,
        history: [{ role: "user", content: "Say goodbye in one word" }],
        stream: true,
    });
    for await (const token of result2.tokenStream) {
        process.stdout.write(token);
    }
    console.log("\n");
    await unloadModel({ modelId });
    console.log("✅ Model unloaded");
    process.exit(0);
}
catch (error) {
    console.error("❌ Error:", error);
    process.exit(1);
}

Tip: all examples throughout this documentation are self-contained and runnable. For instructions on how to run them, see SDK quickstart.

On this page