Punch list item 3: UNATTENDED-BIRTH call site, verified live end-to-end
Implements the unattended-birth mechanism FABRIC-3.md §XXXII.2 designed: UNATTENDED-BIRTH ( name-c name-u -- ok? ) births a VM from a named (p) capsule via capsule_birth_baby() (completely unmodified, the same generic build-time-capsule path CAPSULE-BIRTH already uses), then installs its identity the same way capsule_wirebind.c already does live for WIREBIND attaches -- vm_identity_from_cert() verification followed by a plain post-birth struct assignment -- rather than anything RUNCAP-shaped, since RUNCAP requires a real blkio_dev+ homeblocks_sig_t an unattended identity never has. The born VM's own capsule payload is expected to lay down two CREATE'd buffers (UNATTENDED-ID-UUID, UNATTENDED-ID-CERT) via MINT-SCRATCH- EMIT's own literal format; their addresses are fetched by interpreting a two-word line inside the *new* VM's own context (vm_interpret(born_vm, ...)), the same "run inside that VM's own dictionary" idiom capsule_wirebind.c already uses for VM-NAME-REG. Explicit invariant preserved: never touches g_wirebind_attached_username or any console-pairing state, births no console VM -- an unattended identity stays un-promptable (§VIII.1) until a human pairs a console to it later via the already-working VM-NAME-REG mechanism. Verified live end-to-end on amd64: minted a real test identity via MINT-SCRATCH, captured its MINT-SCRATCH-EMIT output, built a throwaway test capsule from it (discovered along the way: mkcapsule's real block constraints are range [2048,5120) and max 16 content lines per block -- neither matches this repo's own doc comment, corrected via ground truth from the tool itself, not assumed), then ran UNATTENDED-BIRTH against it: cert verified against Zuse's root pubkey, identity installed, "no console attached" reported, Hera stayed healthy afterward (5 6 + . -> 11). Test capsule reverted after capture per this project's own probe convention -- not a real identity, never committed. Clean 3-arch qemu boot (amd64/aarch64/riscv64) on the real committed C-only change. Remaining punch-list item (the ACL cap bit for console attachment) not started. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BWpNjdwPtFLuVLaAq44L9K
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5fc709a228
commit
5879c8b3bc
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-09-16T09:11:04Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-09-16T11:05:06Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -48,6 +48,7 @@
|
||||
#include "freestanding/stdio.h"
|
||||
#include "starkernel/capsule_mint.h"
|
||||
#include "starkernel/user_identity_seed.h"
|
||||
#include "starkernel/vm_identity.h"
|
||||
#include "starkernel/zuse_eligibility.h"
|
||||
#include "starkernel/capsule_loader.h"
|
||||
#include "starkernel/capsule_run.h"
|
||||
@@ -1613,6 +1614,124 @@ void mama_word_capsule_birth(VM *vm)
|
||||
vm_push(vm, (cell_t)new_vm_id.hi);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UNATTENDED-BIRTH ( name-c name-u -- ok? )
|
||||
* Birth an unattended identity from a named (p) capsule -- the
|
||||
* punch-list item 3 call site (FABRIC-3.md §XXXII.2, 2026-09-16):
|
||||
* capsule_birth_baby() unmodified (same generic build-time-capsule
|
||||
* birth path CAPSULE-BIRTH above already uses), then a post-birth
|
||||
* identity install matching capsule_wirebind.c's own proven pattern
|
||||
* (verified cert -> plain struct assignment onto the born VM's own
|
||||
* identity field) rather than anything RUNCAP-shaped -- RUNCAP requires
|
||||
* a real blkio_dev+homeblocks_sig_t an unattended identity never has.
|
||||
*
|
||||
* The capsule's own IDENTITY code (run as part of capsule_birth_baby()
|
||||
* itself) is expected to have already laid down two CREATE'd buffers
|
||||
* in the new VM's own dictionary -- UNATTENDED-ID-UUID (16 bytes) and
|
||||
* UNATTENDED-ID-CERT (4096 bytes), exactly the shape MINT-SCRATCH-EMIT
|
||||
* prints for hand-transcription. Their addresses are fetched by
|
||||
* interpreting a two-word line inside the *new* VM's own context
|
||||
* (vm_interpret(born_vm, ...)) -- the same "run inside that VM's own
|
||||
* dictionary" idiom capsule_wirebind.c already uses for VM-NAME-REG,
|
||||
* not a new mechanism.
|
||||
*
|
||||
* Explicit invariant (FABRIC-3.md §XXXII.2's own ratified text): this
|
||||
* word never touches g_wirebind_attached_username or any other
|
||||
* WIREBIND/console-pairing state, and births no console VM -- an
|
||||
* unattended identity stays un-promptable (§VIII.1) until a human
|
||||
* pairs a console to it later via the existing VM-NAME-REG mechanism,
|
||||
* a separate, already-working step this word does not perform.
|
||||
*/
|
||||
static void mama_word_unattended_birth(VM *vm)
|
||||
{
|
||||
char capsule_name[VM_NAME_MAX];
|
||||
cell_t u, caddr;
|
||||
uint32_t i;
|
||||
|
||||
if (vm->dsp < 1) {
|
||||
console_println("UNATTENDED-BIRTH: expects S\" capsule-name\" UNATTENDED-BIRTH");
|
||||
vm->error = 1;
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
u = vm_pop(vm);
|
||||
caddr = vm_pop(vm);
|
||||
if (u <= 0 || (uint32_t)u >= VM_NAME_MAX) {
|
||||
console_println("UNATTENDED-BIRTH: capsule name too long or empty");
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
{
|
||||
const uint8_t *p = vm_ptr(vm, (vaddr_t)caddr);
|
||||
if (!p) {
|
||||
console_println("UNATTENDED-BIRTH: invalid address on the stack");
|
||||
vm->error = 1;
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < (uint32_t)u; i++) capsule_name[i] = (char)p[i];
|
||||
}
|
||||
capsule_name[u] = '\0';
|
||||
|
||||
if (!vm->zuse_root_pubkey_known) {
|
||||
console_println("UNATTENDED-BIRTH: refused -- Zuse's root pubkey not yet loaded");
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
VMUuid new_vm_id = vm_uuid_none();
|
||||
void *new_vm_ctx = (void *)0;
|
||||
CapsuleRunResult result = capsule_birth_baby(
|
||||
capsule_name,
|
||||
capsule_get_directory(),
|
||||
capsule_get_descriptors(),
|
||||
capsule_get_names(),
|
||||
capsule_get_arena(),
|
||||
vm->stadium_vm_id,
|
||||
0, /* skip_pki_sig: normal build-time capsule, same as CAPSULE-BIRTH */
|
||||
&new_vm_id,
|
||||
&new_vm_ctx
|
||||
);
|
||||
if (result != CAPSULE_RUN_OK || !new_vm_ctx) {
|
||||
console_println("UNATTENDED-BIRTH: birth FAILED");
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
VM *born_vm = (VM *)new_vm_ctx;
|
||||
|
||||
cell_t dsp_before = born_vm->dsp;
|
||||
vm_interpret(born_vm, "UNATTENDED-ID-UUID UNATTENDED-ID-CERT");
|
||||
if (born_vm->error || born_vm->dsp != dsp_before + 2) {
|
||||
console_println("UNATTENDED-BIRTH: refused -- capsule never defined "
|
||||
"UNATTENDED-ID-UUID/UNATTENDED-ID-CERT");
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
cell_t cert_addr = vm_pop(born_vm);
|
||||
cell_t uuid_addr = vm_pop(born_vm);
|
||||
|
||||
const uint8_t *cert = vm_ptr(born_vm, (vaddr_t)cert_addr);
|
||||
const uint8_t *uuid = vm_ptr(born_vm, (vaddr_t)uuid_addr);
|
||||
if (!cert || !uuid) {
|
||||
console_println("UNATTENDED-BIRTH: refused -- invalid identity data address");
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
VMIdentity identity;
|
||||
if (vm_identity_from_cert(&identity, cert, 4096,
|
||||
vm->zuse_root_pubkey, uuid,
|
||||
0 /* acl_caps: no bits assigned yet, §F.2 */) != 0) {
|
||||
console_println("UNATTENDED-BIRTH: refused -- cert verification FAILED");
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
born_vm->identity = identity;
|
||||
|
||||
console_println("UNATTENDED-BIRTH: identity installed, no console attached");
|
||||
vm_push(vm, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CAPSULE-RUN ( capsule-id -- )
|
||||
* Run an experiment (e) capsule on Mama.
|
||||
@@ -1953,6 +2072,7 @@ void register_mama_forth_words(VM *vm)
|
||||
register_word(vm, "MINT", mama_word_mint);
|
||||
register_word(vm, "MINT-SCRATCH", mama_word_mint_scratch);
|
||||
register_word(vm, "MINT-SCRATCH-EMIT", mama_word_mint_scratch_emit);
|
||||
register_word(vm, "UNATTENDED-BIRTH", mama_word_unattended_birth);
|
||||
register_word(vm, "ZUSE-ELIGIBILITY-ADD", mama_word_zuse_eligibility_add);
|
||||
register_word(vm, "ZUSE-ELIGIBLE?", mama_word_zuse_eligible_query);
|
||||
register_word(vm, "NAME>XT", mama_word_name_to_xt);
|
||||
@@ -2014,6 +2134,7 @@ void register_mama_forth_words(VM *vm)
|
||||
register_word(vm, "MINT", mama_word_mint);
|
||||
register_word(vm, "MINT-SCRATCH", mama_word_mint_scratch);
|
||||
register_word(vm, "MINT-SCRATCH-EMIT", mama_word_mint_scratch_emit);
|
||||
register_word(vm, "UNATTENDED-BIRTH", mama_word_unattended_birth);
|
||||
register_word(vm, "ZUSE-ELIGIBILITY-ADD", mama_word_zuse_eligibility_add);
|
||||
register_word(vm, "ZUSE-ELIGIBLE?", mama_word_zuse_eligible_query);
|
||||
register_word(vm, "NAME>XT", mama_word_name_to_xt);
|
||||
|
||||
Reference in New Issue
Block a user