Compare commits

..
Author SHA1 Message Date
Quixos Subtree Publisher 4a0eec33d8 Publish quixos-nix-helpers from 286ada5419c4fc2184b387cea4715fae1a74bde5 2026-10-10 00:10:14 +00:00
Timothy J. Aveni d6434efc28 Reuse checked bootstrap catalogs and coordinate execution deadlines 2026-10-09 17:10:14 -07:00
Timothy J. Aveni ccb48afc04 Document package architecture and organize current guidance and project backlog 2026-10-09 10:33:39 -07:00
Quixos Subtree Publisher e4e5c1f175 Publish quixos-nix-helpers from 98cd7bc237027368fd2c77f294f59281ef3df160 2026-10-08 12:33:28 +00:00
Timothy J. Aveni 1b91e21d49 Keep restricted engines reserved through asynchronous retirement 2026-10-08 05:21:10 -07:00
Quixos Subtree Publisher fcfce6259e Publish quixos-nix-helpers from 806ea0e9f41340acf119b7374f5150ef5b378074 2026-10-08 11:31:16 +00:00
Timothy J. Aveni dec3aa475a Expose workspace-scoped restricted artifact placement and live executor activity
Show per-export placement, static eligibility blockers, bounded metadata and live pool/idle/timing diagnostics. Attach inspector updates through one private shard stream and the existing authenticated SSE hub without waking graph views or VMs. Fix packed agent preparation to use OpenSSH and the worker’s own private systemd credential.
2026-10-08 04:31:16 -07:00
4 changed files with 88 additions and 8 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"version": 1,
"sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos",
"sourceCommit": "57684c7548883dc4fc25dc8f943c7a43b3489fcf",
"sourceCommit": "286ada5419c4fc2184b387cea4715fae1a74bde5",
"sourcePath": "quixos-instance/quixos-nix-helpers",
"exportName": "quixos-nix-helpers",
"mirrorRemote": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git"
+15 -1
View File
@@ -1,5 +1,11 @@
# Checked capability and component artifacts
This is the **current helper/output format**, not the core package abstraction.
See [the architecture](../../docs/ARCHITECTURE.md) and
[current gaps](../../docs/ARCHITECTURE_STATUS.md). In particular, the universal
artifact inventory and component-only omission of a server are implementation
gaps to resolve, not requirements for arbitrary package servers.
`mkCaminoArtifacts` is the fresh package builder. It regenerates candidate
bindings, typechecks the entry's dependency closure, bundles declared targets,
generates a process service when requested, and emits content manifests and
@@ -105,7 +111,7 @@ and supplies an explicit SDK for value codecs, identities and declared graph por
service = {
restricted = true;
limits = {
deadlineMs = 4000;
deadlineMs = 10000;
maxBytes = 1048576;
maxPortCalls = 1000;
maxQueued = 8;
@@ -122,6 +128,14 @@ invocation identity; authored code cannot choose another frame or workspace.
Native graph grants and transaction commit remain enforced by Camino outside
the guest. Initialization and synchronous loops are subject to the same deadline.
The default root/Package Invocation wall deadline is 10 seconds. The separate
root CPU allowance remains at most 4 CPU seconds; extra wall time accommodates
queueing and graph-port waits. Ordinary graph database transactions and idle
transactions allow 30 seconds, and ordinary client HTTP requests allow 45 seconds
so server failures can be returned. Nested calls inherit the remaining root
budget; explicit smaller limits still apply. Restore/activation jobs retain their
separate budgets. These defaults are starting limits, not latency targets.
Direct network access, native filesystem imports, ambient clock and entropy,
parent environment credentials, and unlisted modules are denied. Explicit dates,
`Intl.Segmenter`, deterministic hashes and normal JavaScript computation remain
+22 -5
View File
@@ -108,7 +108,7 @@ export async function serveRestrictedRegistry(config, io = process) {
const limits = {
maxBytes: config.maxBytes ?? 1024 * 1024,
maxPortCalls: config.maxPortCalls ?? 1000,
deadlineMs: config.deadlineMs ?? 4000,
deadlineMs: config.deadlineMs ?? 10000,
maxQueued: config.maxQueued ?? 8,
maxActive: config.maxActive ?? 2,
maxNested: config.maxNested ?? 4,
@@ -148,6 +148,11 @@ export async function serveRestrictedRegistry(config, io = process) {
engines = new Set(),
idleEngines = [];
const metrics = { starts: 0, reused: 0, completed: 0, peakRSSBytes: 0 };
const changed = () => {
try {
config.onChange?.();
} catch {}
};
let buffered = Buffer.alloc(0),
stopped = false,
failure;
@@ -234,16 +239,19 @@ export async function serveRestrictedRegistry(config, io = process) {
}
const engine = call.engine;
if (engine) {
// Revoke the frame token before returning an engine to the idle pool.
// A delayed child request can never acquire the next call's authority.
if (engine.current === call) engine.current = null;
if (!stopped && reason.code === "INVOCATION_ENDED" && !usageFailure && !engine.destroying) {
const rss = (await engineUsage(engine)).memoryBytes;
metrics.peakRSSBytes = Math.max(metrics.peakRSSBytes, rss);
// Bound retained engine heaps as well as lifetime. Hard aggregate
// physical memory remains the supervising process/cgroup's contract.
if (rss <= 128 * 1024 * 1024) {
// Keep the lease busy through every await. Another artifact can evict
// an idle lease immediately; never publish an engine still retiring
// or one whose process died while its final usage read was pending.
if (rss <= 128 * 1024 * 1024 && !stopped && !engine.destroying && engine.current === call) {
// Revoke the frame token atomically with publishing idle capacity.
engine.current = null;
idleEngines.push(engine);
engine.idleUntil = Date.now() + limits.engineIdleMs;
engine.idleTimer = setTimeout(() => void destroyEngine(engine).catch(fail), limits.engineIdleMs);
} else await destroyEngine(engine);
} else await destroyEngine(engine);
@@ -300,6 +308,7 @@ export async function serveRestrictedRegistry(config, io = process) {
} finally {
engines.delete(engine);
await engine.release?.();
changed();
}
})();
return engine.destroying;
@@ -418,6 +427,7 @@ export async function serveRestrictedRegistry(config, io = process) {
},
);
metrics.starts++;
changed();
engine.exited = new Promise((resolve) => {
engine.child.once("exit", resolve);
engine.child.once("error", resolve);
@@ -462,6 +472,9 @@ export async function serveRestrictedRegistry(config, io = process) {
executionMs: call.executionMs ?? null,
totalMs: performance.now() - call.receivedAt,
};
metrics.lastFailure =
reply.status === "failed" && /^[A-Z0-9_]{1,128}$/.test(reply.error?.code) ? reply.error.code : null;
changed();
send(call.cancellationReply ?? reply);
pump();
})());
@@ -470,6 +483,7 @@ export async function serveRestrictedRegistry(config, io = process) {
const { message } = call;
call.startedAt = performance.now();
active.set(message.invocation, call);
changed();
const terminate = (code) => {
if (!call.open) return;
void finish(call, fixedError(code), {
@@ -687,6 +701,9 @@ export async function serveRestrictedRegistry(config, io = process) {
queued: queued.length,
engines: engines.size,
idle: idleEngines.length,
idleDeadline: idleEngines.length
? new Date(Math.min(...idleEngines.map((engine) => engine.idleUntil))).toISOString()
: null,
}),
};
}
+50 -1
View File
@@ -570,12 +570,25 @@ test("an expired frame token cannot borrow a later invocation's broker", async (
});
test("idle engines evict without a graph poll and restart with fresh state", async () => {
const f = await fixture(registry("return request.input;"), { engineIdleMs: 30 });
const updates = [];
const f = await fixture(registry("return request.input;"), {
engineIdleMs: 30,
onChange: () => updates.push(f.stats()),
});
try {
f.start("first");
assert.equal((await f.receive()).output, "first");
await new Promise((resolve) => setTimeout(resolve, 100));
assert.equal(f.stats().engines, 0);
assert.ok(
updates.some((s) => s.active > 0),
"Inspectors observe active invocation admission",
);
assert.ok(
updates.some((s) => s.idle > 0 && s.idleDeadline),
"Idle eviction deadline is visible",
);
assert.equal(updates.at(-1).engines, 0, "Eviction pushes an update without a graph poll");
f.start("second");
assert.equal((await f.receive()).output, "second");
assert.equal(f.stats().starts, 2);
@@ -673,3 +686,39 @@ test("the shared engine budget evicts idle code families and rejects busy oversu
}
assert.equal(engineBudget.stats().engines, 0);
});
test("engine retirement accounting keeps its admission lease busy", async () => {
const observations = [];
let idle;
const engineBudget = new RestrictedEngineBudget(1, async () => ({
usage: async () => {
observations.push(idle());
await new Promise((done) => setImmediate(done));
return { cpuMicroseconds: 0, memoryBytes: 1024 };
},
release: async () => {},
}));
const acquire = engineBudget.acquire.bind(engineBudget);
engineBudget.acquire = (isIdle, evict) => {
idle = isIdle;
return acquire(isIdle, evict);
};
const f = await fixture(registry("return request.input;"), { engineBudget });
try {
f.start("first");
assert.equal((await f.receive()).output, "first");
assert.ok(observations.length > 0);
assert.ok(
observations.every((value) => !value),
"An engine must not become evictable during asynchronous retirement accounting",
);
assert.equal(f.stats().idle, 1);
assert.equal(idle(), true);
f.start("second");
assert.equal((await f.receive()).output, "second");
assert.equal(f.stats().starts, 1);
} finally {
await f.close();
}
assert.equal(engineBudget.stats().engines, 0);
});