Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ ASSEMBLY_SOURCES = $(wildcard $(KERNEL_DIR)/src/*.S)
# objects (symbols _binary_<name>_elf_start/_end)
USER_DIR = user
USER_BUILD = $(BUILD_DIR)/user
USER_PROGS = init echo client hbsvc ghostd ghost_test paradoxd paradox_test swarm_svc qsh quantumd kannakad fieldsyncd httpd quota_test delegation_test subagentd cpu_hog qsv qpud qpu_test
USER_PROGS = init echo client hbsvc ghostd ghost_test paradoxd paradox_test swarm_svc qsh quantumd kannakad fieldsyncd httpd quota_test delegation_test subagentd cpu_hog qsv qpud qpu_test agentd agentsub
USER_ELF_OBJS = $(USER_PROGS:%=$(USER_BUILD)/%_elf.o)

# libq: the freestanding ring-3 runtime, built as a static archive and linked
Expand Down Expand Up @@ -1065,6 +1065,22 @@ ci-smoke: kernel
echo ""; echo "=== Smoke Test FAILED ==="; exit 1; \
fi
@echo "SUCCESS: capability delegation proven (narrowed cap enforced + cascade-revoked; no BROKEN)"
@# Agent-native end-to-end demo (the mission showcase): a single ring-3 citizen
@# runs a QPU job through the broker, imprints+recalls holographic field memory,
@# spawns a sub-process, AND delegates a narrowed field cap to its sub-agent —
@# all four verified. The one gate line prints only if EVERY step succeeded.
@if ! grep -q "AGENTD: DEMO OK qpu+field+spawn+delegate" /tmp/qemu-boot.log 2>/dev/null; then \
echo "ERROR: agent-native end-to-end demo did not complete (AGENTD: DEMO OK)"; \
grep -E "AGENT BROKEN|AGENTD: DEMO BROKEN|AGENTSUB BROKEN" /tmp/qemu-boot.log 2>/dev/null || true; \
echo "Boot log:"; cat /tmp/qemu-boot.log 2>/dev/null || true; \
echo ""; echo "=== Smoke Test FAILED ==="; exit 1; \
fi
@if grep -qE "AGENT BROKEN|AGENTD: DEMO BROKEN|AGENTSUB BROKEN" /tmp/qemu-boot.log 2>/dev/null; then \
echo "ERROR: agent demo reported a BROKEN step"; \
grep -E "AGENT BROKEN|AGENTD: DEMO BROKEN|AGENTSUB BROKEN" /tmp/qemu-boot.log 2>/dev/null || true; \
echo ""; echo "=== Smoke Test FAILED ==="; exit 1; \
fi
@echo "SUCCESS: agent-native demo proven end to end (QPU + field + spawn + delegate)"
@# Supervision gate: after the piped 'exit', the watchdog must restart the
@# shell, which reintroduces itself as reborn.
@if ! grep -q "QSH: reborn" /tmp/qemu-boot.log 2>/dev/null; then \
Expand Down
16 changes: 16 additions & 0 deletions docs/RUNTIME.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,22 @@ single-basin binary attractor that relaxes onto one pattern, the kernel
field kannakad speaks to is the ranked, importance-weighted cousin. A
ci-smoke gate asserts all three behaviours (`RESONANCE VERIFIED`).

`user/agentd.c` (+ its capless sub-agent `user/agentsub.c`) is the **agent-native
end-to-end demo** — one ring-3 citizen exercising the whole agentic stack in a
single story, each step with a verifiable outcome (agentd carries no engine, so
a correct result can only have come back through the kernel's brokers): it
**submits a Bell circuit** through the `SYS_QPU` broker and checks `qpud`'s exact
`p(00) = 1/2`; **imprints and recalls** a phrase in holographic field region 3
from a deliberately corrupted probe; **spawns** `/bin/hello` and waits for it to
exit (real spawn authority); and **delegates** a strictly-narrowed READ slice of
its region-3 field cap to `agentsub` via `SYS_CAP_DERIVE` over a
capability-checked IPC pair, which the sub-agent recalls with (and confirms WRITE
was withheld). On all four it prints `AGENTD: DEMO OK`; a ci-smoke gate asserts
it. Being a heavyweight showcase, it runs only on an interactive/non-`quiet`
boot (the default ci-smoke and the non-quiet ISO entry) — under `quiet` (the
minimal-boot flag the MCP/society CI gates use) it is skipped so its extra boot
work never perturbs their timing-sensitive proofs.

`user/quantumd.c` is the essence of `kannaka-quantum`, and — unlike the two
above — a **kernel-embedded service**, not a `/bin` program. `SYS_QRAND` and
`SYS_QSEED` are capability-gated (a capless `/bin` caller gets EPERM by
Expand Down
79 changes: 79 additions & 0 deletions kernel/src/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ extern const uint8_t _binary_cpu_hog_elf_start[], _binary_cpu_hog_elf_end[];
extern const uint8_t _binary_qsv_elf_start[], _binary_qsv_elf_end[];
extern const uint8_t _binary_qpud_elf_start[], _binary_qpud_elf_end[];
extern const uint8_t _binary_qpu_test_elf_start[], _binary_qpu_test_elf_end[];
extern const uint8_t _binary_agentd_elf_start[], _binary_agentd_elf_end[];
extern const uint8_t _binary_agentsub_elf_start[], _binary_agentsub_elf_end[];

/* Argument vector ABI (epic #62). MUST stay byte-identical to user_args_t
* in user/usys.h — there is no shared header across the ring boundary. The
Expand Down Expand Up @@ -94,6 +96,7 @@ void user_cpu_hog_init(void);
void user_qsv_init(void);
void user_qpud_init(void);
void user_qpu_test_init(void);
void user_agent_demo_init(void);

/* int 0x80 stub (kernel/src/interrupts.S) */
extern void isr128(void);
Expand Down Expand Up @@ -2133,6 +2136,9 @@ void user_init(void) {
* already fetch-ready (epic #148). */
user_qpud_init();
user_qpu_test_init();
/* The agent-native end-to-end demo — after qpud so its QPU step can execute.
* Its own sub-agent is started + IPC-wired inside. */
user_agent_demo_init();
}

/* Bring up quantumd — a quantum-pool service (kannaka-quantum, a fourth
Expand Down Expand Up @@ -2783,3 +2789,76 @@ void user_qpu_test_init(void) {
boot_log("Warning: qpu-test service failed to start");
}
}

/* The agent-native end-to-end demo (the mission showcase). agentd holds the four
* grants its story needs — QPU submit (qsub quota), field region 3, the CAP_GRANT
* to DELEGATE that region, and spawn — and its sub-agent agentsub is capless. As
* with the delegation demo, both are registered + started here and a bidirectional
* capability-checked IPC pair is minted so agentd can SYS_CAP_DERIVE to agentsub
* (the IPC-peer requirement) and each can send/recv. Neither is monitored: they
* run their one-shot proof and idle, so the derived cap's cascade-revoke provenance
* (and the AGENTD gate line) stay stable. Both spawn READY but do not run until the
* timer starts after user_init, so the IPC caps exist before either sends. */
void user_agent_demo_init(void) {
/* The demo is a heavyweight SHOWCASE (an extra QPU job, a spawn, a field
* imprint/recall, and a delegation handshake). Run it only on an interactive/
* showcase boot, NOT under `quiet` — the minimal-boot flag the specialized CI
* gates (MCP, society, quiet) use. There its added boot work would delay
* other timing-sensitive proofs (e.g. the delegation-reap the MCP gate polls
* for) without those gates ever checking the demo. The default ci-smoke boot
* (no `quiet`) and the non-quiet ISO entry still run + gate it. */
if (boot_is_quiet()) {
boot_log("agentd: end-to-end demo skipped (quiet boot)");
return;
}
service_definition_t agentsub_def = {
.name = "agentsub",
.entry = NULL, /* user-process service; NO grants — capless by design */
.user_elf_start = _binary_agentsub_elf_start,
.user_elf_end = _binary_agentsub_elf_end,
.dependencies = {NULL},
.max_restarts = 1,
};
service_definition_t agentd_def = {
.name = "agentd",
.entry = NULL,
.user_elf_start = _binary_agentd_elf_start,
.user_elf_end = _binary_agentd_elf_end,
.dependencies = {"qpud", NULL}, /* its QPU step needs the executor fetch-ready */
.max_restarts = 1,
.grant_qpu_submit = 1,
.qsub_max = 4,
.grant_spawn = 1,
.spawn_max = 2,
/* Sole CAP_GRANT holder for region 3: READ|WRITE|GRANT, so it can imprint,
* recall, and delegate a narrowed READ slice. */
.grant_field = 1,
.field_region = 3,
.grant_field_delegable = 1,
};

uint32_t sub_sid = 0, ag_sid = 0, sub_pid = 0, ag_pid = 0;
service_info_t info;
if (service_register(&agentsub_def, &sub_sid) == SVC_SUCCESS &&
service_start("agentsub", NULL) == SVC_SUCCESS &&
service_status(sub_sid, &info) == SVC_SUCCESS) {
sub_pid = info.pid;
}
if (service_register(&agentd_def, &ag_sid) == SVC_SUCCESS &&
service_start("agentd", NULL) == SVC_SUCCESS &&
service_status(ag_sid, &info) == SVC_SUCCESS) {
ag_pid = info.pid;
}
if (sub_pid == 0 || ag_pid == 0) {
boot_log("Warning: agent demo failed to start");
return;
}

/* Bidirectional capability-checked IPC (the delegation-demo pattern): agentd
* can reach agentsub (the SYS_CAP_DERIVE IPC-peer requirement) and back. */
uint32_t cap = CAP_ID_INVALID;
cap_create(ag_pid, CAP_RESOURCE_IPC, sub_pid, CAP_READ | CAP_WRITE, 0, &cap);
cap_create(sub_pid, CAP_RESOURCE_IPC, ag_pid, CAP_READ | CAP_WRITE, 0, &cap);

boot_log("agentd: agent-native end-to-end demo (QPU + field + spawn + delegate)");
}
230 changes: 230 additions & 0 deletions user/agentd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/**
* QuantumOS agentd — the agent-native end-to-end demo (the mission showcase).
*
* One ring-3 citizen that exercises the whole agentic stack in a single story,
* each step with a verifiable outcome (agentd carries no engine of its own — the
* results can only have come back through the kernel's brokers):
*
* 1. QPU — submits a Bell circuit through the SYS_QPU broker and checks
* the EXACT probability qpud computed, p(00) = 1/2.
* 2. FIELD — imprints a phrase into holographic field region 3 and recalls
* it from a deterministically CORRUPTED probe (wave-interference
* associative memory), verifying the exact stored bytes return.
* 3. SPAWN — starts /bin/hello as a sub-process and waits for it to exit
* (real spawn authority — only a citizen holding the cap can).
* 4. DELEGATE — hands a strictly-NARROWED READ-only slice of its region-3 field
* cap to a sub-agent (agentsub) via SYS_CAP_DERIVE over a
* capability-checked IPC pair; the sub-agent recalls with it and
* acks. This is "an agent hands a narrowed intent to a sub-agent."
*
* On all four it prints the single CI merge-gate line
* AGENTD: DEMO OK qpu+field+spawn+delegate
* Grants: qpu_submit(quota) + field(region 3) + field_delegable + spawn. NOT
* monitored (a watchdog respawn would re-run the one-shot proof); idles after.
*
* SPDX-License-Identifier: GPL-2.0-only
*/

#include "libq/libq.h"
#include "usys.h"
#include "qpu_circuit.h"

#define AGENT_REGION 3
static const unsigned char AGENT_PHRASE[] = "agentd end to end field phrase";
#define AGENT_PHRASE_LEN (sizeof(AGENT_PHRASE) - 1)

static unsigned int get_u32(const unsigned char *p) {
return (unsigned int)p[0] | ((unsigned int)p[1] << 8) | ((unsigned int)p[2] << 16) |
((unsigned int)p[3] << 24);
}

/* Submit an opaque circuit and poll it to completion (the qpu_test pattern).
* Returns the job id (>0) with the DONE result in *out, or <=0 on failure. */
static long submit_and_wait(const unsigned char *circuit, unsigned int len, qpu_poll_out_t *out) {
qpu_submit_req_t req;
req.circuit_len = len;
for (unsigned int i = 0; i < len; i++) {
req.circuit[i] = circuit[i];
}
long jid = qpu_submit_(&req);
if (jid <= 0) {
return jid;
}
for (int spins = 0; spins < 2000000; spins++) {
long st = qpu_poll_(jid, out);
if (st == QPU_POLL_DONE) {
return jid;
}
if (st < 0) {
return st;
}
yield();
}
return -100; /* timed out waiting for qpud */
}

/* Step 1 — a Bell pair through the broker: p(00) must be exactly 1/2. */
static int do_qpu(void) {
unsigned char bell[QC_HDR + 2 * 3];
bell[0] = QC_VERSION;
bell[1] = 2; /* qubits */
bell[2] = 2; /* ops */
bell[3] = 0; /* probe |00> */
bell[4] = 0;
bell[5] = bell[6] = bell[7] = 0;
bell[8] = QC_OP_H;
bell[9] = 0;
bell[10] = 0;
bell[11] = QC_OP_CNOT;
bell[12] = 0;
bell[13] = 1;
qpu_poll_out_t out;
long j = submit_and_wait(bell, sizeof(bell), &out);
if (j <= 0 || out.status != QPU_STATUS_OK || out.result_len != QC_RESULT_LEN) {
printf("AGENT BROKEN qpu submit/poll j=%ld\n", j);
return 0;
}
unsigned int num = get_u32(out.result + 4);
unsigned int den = get_u32(out.result + 8);
if (num == 1 && den == 2) {
printf("AGENT: QPU bell via broker job=%ld p=1/2 (exact)\n", j);
return 1;
}
printf("AGENT BROKEN qpu p=%u/%u\n", num, den);
return 0;
}

/* Step 2 — imprint region 3, then recall it from a corrupted probe. */
static int do_field(void) {
field_imprint_req_t ireq;
for (unsigned i = 0; i < sizeof(ireq); i++) {
((unsigned char *)&ireq)[i] = 0;
}
ireq.region = AGENT_REGION;
ireq.len = AGENT_PHRASE_LEN;
for (unsigned n = 0; n < AGENT_PHRASE_LEN; n++) {
ireq.pattern[n] = AGENT_PHRASE[n];
}
if (imprint_(&ireq) < 0) {
write_str("AGENT BROKEN field imprint");
return 0;
}

field_recall_req_t rreq;
field_recall_out_t rout;
for (unsigned i = 0; i < sizeof(rreq); i++) {
((unsigned char *)&rreq)[i] = 0;
}
rreq.region = AGENT_REGION;
rreq.len = AGENT_PHRASE_LEN;
rreq.k = 1;
for (unsigned n = 0; n < AGENT_PHRASE_LEN; n++) {
rreq.probe[n] = AGENT_PHRASE[n];
}
rreq.probe[1] ^= 0x20; /* corrupt two bytes — the probe is deliberately noisy */
rreq.probe[5] ^= 0x20;
if (recall_(&rreq, &rout) != 0 || rout.winner_len != AGENT_PHRASE_LEN) {
write_str("AGENT BROKEN field recall");
return 0;
}
for (unsigned n = 0; n < AGENT_PHRASE_LEN; n++) {
if (rout.winner[n] != AGENT_PHRASE[n]) {
write_str("AGENT BROKEN field winner mismatch");
return 0;
}
}
write_str("AGENT: field recalled the phrase from a noisy probe");
return 1;
}

/* Step 3 — spawn /bin/hello and wait for it to exit. */
static int do_spawn(void) {
long pid = spawn_("/bin/hello");
if (pid <= 0) {
printf("AGENT BROKEN spawn=%ld\n", pid);
return 0;
}
for (long spins = 0; spins < 8000000L; spins++) {
long w = waitpid_(pid);
if (w != WAITPID_RUNNING) {
printf("AGENT: spawned /bin/hello pid=%ld exit=%ld\n", pid, w);
return 1;
}
yield();
}
write_str("AGENT BROKEN spawn waitpid timeout");
return 0;
}

/* Step 4 — delegate a narrowed READ cap over region 3 to agentsub. */
static int do_delegate(void) {
char buf[16];
long sub = 0;
for (long spins = 0; spins < 8000000L && !sub; spins++) {
long s = recv_msg(buf, sizeof(buf));
if (s != 0) {
sub = s; /* the kernel vouches the sender pid */
} else {
yield();
}
}
if (!sub) {
write_str("AGENT BROKEN delegate no sub ready");
return 0;
}

cap_derive_req_t req;
for (unsigned i = 0; i < sizeof(req); i++) {
((unsigned char *)&req)[i] = 0;
}
req.resource_type = CAP_RESOURCE_FIELD;
req.resource_id = AGENT_REGION;
req.permissions = CAP_READ; /* narrowed: no WRITE, no GRANT */
req.target_pid = (unsigned)sub;
req.expiration = 0;
long d = cap_derive_(&req);
if (d != 0) {
printf("AGENT BROKEN cap_derive=%ld\n", d);
return 0;
}
send_msg("go", 2); /* the cap is ready — the sub-agent may recall now */

int proven = 0;
for (long spins = 0; spins < 40000000L && !proven; spins++) {
if (recv_msg(buf, sizeof(buf)) != 0 && buf[0] == 'p') {
proven = 1;
} else {
yield();
}
}
if (!proven) {
write_str("AGENT BROKEN delegate no proven ack");
return 0;
}
printf("AGENT: delegated region 3 (READ) to sub=%ld\n", sub);
return 1;
}

void _start(void) {
/* Region 3 is imprinted in do_field() BEFORE do_delegate() hands the cap
* over, so the sub-agent's recall has content. do_qpu/do_spawn yield, giving
* the sub-agent time to send its buffered "ready". */
int q = do_qpu();
int f = do_field();
int s = do_spawn();
int d = do_delegate();

if (q && f && s && d) {
write_str("AGENTD: DEMO OK qpu+field+spawn+delegate");
} else {
printf("AGENTD: DEMO BROKEN qpu=%d field=%d spawn=%d deleg=%d\n", q, f, s, d);
}
/* One-shot proof: EXIT rather than idle-spin. A TERMINATED process leaves the
* scheduler's ready queue, so the demo does not keep the idle-loop reaper (and
* every other citizen's death cleanup) starved by an ever-ready yield loop.
* Unmonitored, so it is reaped, not restarted; its caps cascade-revoke. */
exit_(0);
for (;;) {
yield();
}
}
Loading
Loading