ACK/NACK and private-channel negotiation -- FABRIC-3.6.md task 3.6

Extracted sk_hermes_send_one() from sk_hermes_publish()'s own
per-subscriber body -- one code path for both point-to-point and
fan-out delivery, so the ledger can never diverge between them.
Point-to-point addressing turned out to be load-bearing, not
incidental: sk_hermes_publish()'s fan-out sets msg->to to whichever
member it is iterating, so a negotiation message "published" to the
common channel would spuriously reach every member, not just the real
target (checked with advisor() before building the naive version).
"Over the common channel" means every VM is reachable from birth (task
3.2), not that the exchange itself fans out -- messaging.4th's own
CH-REQUEST carried an explicit `to` for the same reason.

sk_hermes_channel_request/respond/close build the mechanics: request ->
grant (creates a private channel, subscribes both parties, sends
CH_GRANT + one ACK) or NACK ("a deny is a NACK", SXLV.1 -- no separate
type); close authorized by membership alone. The grant/deny decision is
a plain caller-supplied `approved` bool -- task 3.7 replaces the call
site that produces it with a real ACL.4th query, not this signature.

Self-test covers the task's own three checks plus a sibling advisor()
flagged: an approved respond() whose channel creation itself fails
(table exhausted) must still fall through to NACK, not a silent false
grant or half-open channel -- verified by exhausting the whole channel
table and confirming the fallback.

Bug found and fixed before this was called done: the first draft
dropped a message via pending_pop() alone, without releasing it first,
leaking its Stadium heat and failing the self-test's own ledger
baseline check (logs/20260922-065946/amd64/, kept as audit trail).
Fixed and re-verified PASS on all three architectures.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-22 07:25:51 -04:00
co-authored by Claude Sonnet 5
parent 8a2ee0fdad
commit 205a49ecd0
8 changed files with 37482 additions and 44 deletions
+85 -31
View File
@@ -481,6 +481,42 @@ static int queue_push(SkHermesPendingQueue *q, int msg_index) {
return 0;
}
int sk_hermes_send_one(VMUuid from, VMUuid to, uint32_t type, uint32_t channel_id,
void *payload_addr, uint32_t payload_len) {
SkHermesMessage *msg;
SkHermesPendingQueue *q;
int idx;
/* Task 3.5: the one-block bound, enforced uniformly here whether or
* not payload_addr happens to be a chunk carrier -- see
* kernel_hermes.h's own sizing note on why SK_HERMES_CHUNK_MAX_SLICE
* (not SK_HERMES_CHUNK_MAX_PAYLOAD) is a chunk's own content size. */
if (payload_len > SK_HERMES_CHUNK_MAX_PAYLOAD) return -1;
if (sk_hermes_alloc(from, &msg) != 0) return -1;
msg->type = type;
msg->from = from;
msg->to = to;
msg->payload_addr = payload_addr;
msg->payload_len = payload_len;
msg->channel = channel_id;
q = find_or_create_queue(to);
if (!q) {
sk_hermes_release(msg); /* roll back -- no destination queue */
return -1;
}
idx = (int)(msg - sk_hermes_msgs);
if (queue_push(q, idx) != 0) {
sk_hermes_release(msg); /* roll back -- destination queue full */
return -1;
}
return 0;
}
int sk_hermes_publish(VMUuid from, int channel_id, uint32_t type,
void *payload_addr, uint32_t payload_len) {
SkHermesMembership *m;
@@ -488,41 +524,18 @@ int sk_hermes_publish(VMUuid from, int channel_id, uint32_t type,
int delivered = 0;
if (!channel_valid(channel_id)) return -1;
/* Task 3.5: the one-block bound, enforced uniformly here whether or
* not payload_addr happens to be a chunk carrier -- see
* kernel_hermes.h's own sizing note on why SK_HERMES_CHUNK_MAX_SLICE
* (not SK_HERMES_CHUNK_MAX_PAYLOAD) is a chunk's own content size. */
if (payload_len > SK_HERMES_CHUNK_MAX_PAYLOAD) return -1;
if (payload_len > SK_HERMES_CHUNK_MAX_PAYLOAD) return -1; /* see sk_hermes_send_one()'s
* own check -- duplicated here
* so publish() itself returns
* -1 (not a silent 0) on an
* oversized payload */
m = &sk_hermes_channels[channel_id].membership;
for (i = 0; i < m->count; i++) {
VMUuid to = m->members[i];
SkHermesMessage *msg;
SkHermesPendingQueue *q;
int idx;
if (sk_hermes_alloc(from, &msg) != 0) continue; /* this subscriber skipped, not fatal */
msg->type = type;
msg->from = from;
msg->to = to;
msg->payload_addr = payload_addr;
msg->payload_len = payload_len;
msg->channel = (uint32_t)channel_id;
q = find_or_create_queue(to);
if (!q) {
sk_hermes_release(msg); /* roll back this subscriber's allocation */
continue;
if (sk_hermes_send_one(from, m->members[i], type, (uint32_t)channel_id,
payload_addr, payload_len) == 0) {
delivered++;
}
idx = (int)(msg - sk_hermes_msgs);
if (queue_push(q, idx) != 0) {
sk_hermes_release(msg); /* this subscriber's own queue is full */
continue;
}
delivered++;
}
return delivered;
}
@@ -659,4 +672,45 @@ int sk_hermes_reassemble(SkHermesMessage **chunks, int n_chunks,
return 0;
}
int sk_hermes_channel_request(VMUuid requester, VMUuid target) {
return sk_hermes_send_one(requester, target, SK_HERMES_MSG_TYPE_CH_REQUEST,
SK_HERMES_CHANNEL_COMMON, (void *)0, 0);
}
int sk_hermes_channel_respond(VMUuid target, VMUuid requester, int approved) {
int ch = -1;
if (approved) {
ch = sk_hermes_channel_create();
if (ch >= 0) {
if (sk_hermes_channel_subscribe(ch, requester) != 0 ||
sk_hermes_channel_subscribe(ch, target) != 0) {
sk_hermes_channel_destroy(ch); /* leave no half-open channel */
ch = -1;
}
}
}
if (ch >= 0) {
sk_hermes_send_one(target, requester, SK_HERMES_MSG_TYPE_CH_GRANT,
(uint32_t)ch, (void *)0, 0);
/* Ruled ACK cadence (2026-09-21): channel-open + delivery, not
* every message -- this IS that moment. */
sk_hermes_send_one(target, requester, SK_HERMES_MSG_TYPE_ACK,
(uint32_t)ch, (void *)0, 0);
} else {
/* SXLV.1: "a deny is a NACK" -- no separate CH_DENY type. */
sk_hermes_send_one(target, requester, SK_HERMES_MSG_TYPE_NACK,
SK_HERMES_CHANNEL_COMMON, (void *)0, 0);
}
return ch;
}
int sk_hermes_channel_close(VMUuid closer, int channel_id) {
if (channel_id == SK_HERMES_CHANNEL_COMMON) return -1;
if (!sk_hermes_channel_is_member(channel_id, closer)) return -1;
return sk_hermes_channel_destroy(channel_id);
}
#endif /* __STARKERNEL__ */