FABRIC-3.md §I.4: magic-number content-type detection, closing Milestone 6
Closes both remaining Milestone 6 (PKI) punch-list items. Signature-status column: doc-only closure -- the underlying need was already redirected to capsules/BLOCK_MAP.md's real Signed column (2026-08-26); checking the box off as moot-as-worded rather than leaving an accurate-but-permanently-unchecked marker. Magic-number content-type detection (Section U item 14): built in tools/mkcapsule.c. detect_content_type() classifies a file's actual leading bytes (TTF/OpenType sfnt tags, DER's 0x30 SEQUENCE tag, or a printable-ASCII/TAB/CR/LF heuristic for text) against expected_type_from_ext()'s .4th/.md/.der/.ttf mapping; process_file() warns on mismatch, never refuses -- same WARN-first rollout this project already used for capsule signing. Verified against every real capsule in the repo (38 files) with zero false positives. This is the shared primitive Milestone 7's contrib-capsule validation can reuse next. Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the foreground); logs and DoE CSVs from this session's verification runs included per this repo's own audit-artifact convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
4018fe8b04
commit
5567d03c12
+31
-11
@@ -817,7 +817,7 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
|
||||
call sites in `capsule_birth.c` (lines 472, 551, 747), backed by `capsule_sig.c`. Every
|
||||
capsule in `capsules/BLOCK_MAP.md`'s manifest shows `Signed: yes`.
|
||||
|
||||
- [ ] Extend `MANIFEST_AUTO.md`'s generation to add a signature-status column, matching the
|
||||
- [x] Extend `MANIFEST_AUTO.md`'s generation to add a signature-status column, matching the
|
||||
existing xxHash64 column's generation pattern. **INVESTIGATED 2026-09-03, left unchecked on
|
||||
purpose — redirected, not done as literally worded.** This document's own later text (Phase
|
||||
8 close-out) explicitly decided `MANIFEST_AUTO.md` is "stale/dead, not regenerated since
|
||||
@@ -825,10 +825,14 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
|
||||
real `Signed` column (`| Capsule | Blocks claimed | xxHash64 | Signed |`, values `yes`/`no`
|
||||
per capsule). The underlying goal is met; the specific file this item names deliberately
|
||||
never was touched, so the box stays unchecked as a marker that this exact wording is now
|
||||
moot rather than pretending the literal ask was fulfilled.
|
||||
moot rather than pretending the literal ask was fulfilled. **CLOSED 2026-09-04**: checked
|
||||
off as moot-as-worded, doc-only, per §I.4's matching closure note.
|
||||
|
||||
- [ ] Implement magic-number-based content-type detection (Section U item 14) — a shared
|
||||
primitive, also usable for Milestone 4's foreign-drive check.
|
||||
- [x] Implement magic-number-based content-type detection (Section U item 14) — a shared
|
||||
primitive, also usable for Milestone 4's foreign-drive check. **DONE 2026-09-04**: built as
|
||||
`detect_content_type()`/`expected_type_from_ext()` in `tools/mkcapsule.c`, wired into
|
||||
`process_file()` as a WARN-only extension-vs-actual-bytes cross-check. See §I.4's closure
|
||||
note for the full description.
|
||||
|
||||
- [x] **Root CA + snakeoil intermediate generated 2026-08-26**, entirely offline, in a sibling
|
||||
directory outside this repo (`/home/rajames/CLionProjects/lithosananke-ca/`, not tracked by
|
||||
@@ -981,7 +985,8 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
|
||||
reverted) clean on all three, zero warnings, conservation intact, real disk untouched.
|
||||
|
||||
**Milestone 6 is now fully closed** except magic-number content-type detection (shared with
|
||||
Milestone 4, genuinely separate scope, not started).
|
||||
Milestone 4, genuinely separate scope, not started). **CLOSED 2026-09-04**: see §I.4 —
|
||||
Milestone 6 has no open items left at all now.
|
||||
|
||||
### From FABRIC-2.md §X, Milestone 7 — Contributor capsules / trust tiers
|
||||
|
||||
@@ -4436,16 +4441,31 @@ stale the way the original carry-forwards did.
|
||||
pointer below); verified 3-arch boot to `ok>`. *(Full entry:
|
||||
§A, "From FABRIC-2.md §X, Milestone 5 — Console/VM key-match binding.")*
|
||||
|
||||
### I.4 — Milestone 6 remainder (PKI)
|
||||
### I.4 — Milestone 6 remainder (PKI) — CLOSED 2026-09-04
|
||||
|
||||
- [ ] **`MANIFEST_AUTO.md` signature-status column.** Investigated 2026-09-03, left unchecked
|
||||
- [x] **`MANIFEST_AUTO.md` signature-status column.** Investigated 2026-09-03, left unchecked
|
||||
on purpose: this document's own Phase 8 close-out already redirected the underlying need to
|
||||
`capsules/BLOCK_MAP.md`'s real `Signed` column instead — the goal is met, the literal file
|
||||
this item names was never touched. Box stays open as an accurate "moot as worded" marker,
|
||||
not a real remaining task. *(Full entry: §A, "From FABRIC-2.md §X, Milestone 6 — Kernel/
|
||||
capsule PKI signing chain.")*
|
||||
- [ ] **Magic-number content-type detection.** Not started — shared scope with Milestone 4's
|
||||
foreign-drive check and Milestone 7 below. *(Same location as above.)*
|
||||
not a real remaining task. **Closed 2026-09-04 as exactly that: doc-only, no code** — the
|
||||
box now reflects that the underlying need was already met, rather than leaving an
|
||||
accurate-but-permanently-unchecked marker forever. *(Full entry: §A, "From FABRIC-2.md §X,
|
||||
Milestone 6 — Kernel/capsule PKI signing chain.")*
|
||||
- [x] **Magic-number content-type detection.** Not started — shared scope with Milestone 4's
|
||||
foreign-drive check and Milestone 7 below. **DONE 2026-09-04**: built in `tools/mkcapsule.c`
|
||||
(a host build-time tool, not kernel code — no 3-arch boot dependency for the check itself,
|
||||
though the full acceptance pass still ran since it's on the build's own critical path).
|
||||
`detect_content_type()` classifies a file's actual leading bytes into one of this project's
|
||||
four known capsule content shapes (TTF/OpenType sfnt tags, DER's `0x30` SEQUENCE tag, or a
|
||||
printable-ASCII/TAB/CR/LF heuristic for text); `expected_type_from_ext()` maps `.4th`/`.md`/
|
||||
`.der`/`.ttf` to what they should be. `process_file()` cross-checks the two and **WARNs on
|
||||
mismatch, never refuses** — same WARN-first rollout discipline this project already used for
|
||||
capsule signing (§ Milestone 6 above) before any hard-refuse flip. Verified against every
|
||||
real capsule in this repo (38 files, all four extensions represented) on a full clean 3-arch
|
||||
build: zero false-positive warnings. This is the shared primitive both Milestone 4's
|
||||
foreign-drive check (already independently satisfied by `homeblocks_sig_check()`'s
|
||||
structural check, so not itself a new consumer) and Milestone 7's contrib-capsule validation
|
||||
(§I.5, next) can reuse. *(Same location as above.)*
|
||||
|
||||
### I.5 — Milestone 7 (contributor capsules / trust tiers) — nothing started
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-09-04T14:30:04Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-09-04T14:43:57Z -->
|
||||
<!-- 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
@@ -233,6 +233,75 @@ static uint32_t flags_from_name(const char *name) {
|
||||
return flags;
|
||||
}
|
||||
|
||||
/*
|
||||
* Magic-number content-type detection (FABRIC-3.md §I.4, FABRIC-2.md
|
||||
* Section U item 14). mkcapsule's own header comment has long said "the
|
||||
* file extension is preserved verbatim and is meaningful only at load
|
||||
* time" -- nothing has ever checked that a file's actual bytes match
|
||||
* what its extension claims. This is a cross-check, not a new content
|
||||
* pipeline: process_file() below calls it once per file and WARNS (never
|
||||
* refuses -- same "land WARN-only first" rollout this project already
|
||||
* used for capsule signing) on a mismatch. Deliberately narrow: this
|
||||
* project's own capsule universe is exactly four content shapes today
|
||||
* (FORTH source, markdown, DER binary, TTF/OpenType font), not a general
|
||||
* multimedia magic-number library.
|
||||
*/
|
||||
typedef enum {
|
||||
CONTENT_TYPE_UNKNOWN = 0, /* no expectation, or couldn't classify -- not an error */
|
||||
CONTENT_TYPE_TEXT, /* .4th (FORTH source), .md (markdown) */
|
||||
CONTENT_TYPE_DER, /* .der (X.509 cert / PKCS8 key, both ASN.1 DER) */
|
||||
CONTENT_TYPE_TTF /* .ttf (TrueType) or OpenType sharing the same extension */
|
||||
} content_type_t;
|
||||
|
||||
static const char *content_type_name(content_type_t t) {
|
||||
switch (t) {
|
||||
case CONTENT_TYPE_TEXT: return "text";
|
||||
case CONTENT_TYPE_DER: return "DER binary";
|
||||
case CONTENT_TYPE_TTF: return "TTF/OpenType font";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/* Detects by leading bytes, not extension. TTF/OpenType: the four
|
||||
* well-known sfnt version tags. DER: ASN.1 SEQUENCE tag (0x30) -- every
|
||||
* DER-encoded cert or PKCS8 key in this codebase's own universe (X.509
|
||||
* certs, PKCS8 private keys) opens with one, per the existing
|
||||
* x509_ed25519.c/pkcs8_ed25519.c parsers this mirrors. Text has no fixed
|
||||
* magic -- heuristic: every byte is printable ASCII or TAB/CR/LF; a
|
||||
* capsule embeds source/markdown, never arbitrary binary under those two
|
||||
* extensions, so this heuristic doesn't need to be general-purpose. */
|
||||
static content_type_t detect_content_type(const uint8_t *data, size_t len) {
|
||||
if (len >= 4 &&
|
||||
((data[0] == 0x00 && data[1] == 0x01 && data[2] == 0x00 && data[3] == 0x00) ||
|
||||
memcmp(data, "OTTO", 4) == 0 || memcmp(data, "true", 4) == 0 ||
|
||||
memcmp(data, "ttcf", 4) == 0)) {
|
||||
return CONTENT_TYPE_TTF;
|
||||
}
|
||||
if (len >= 1 && data[0] == 0x30) {
|
||||
return CONTENT_TYPE_DER;
|
||||
}
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint8_t c = data[i];
|
||||
if (c == '\t' || c == '\r' || c == '\n') continue;
|
||||
if (c < 0x20 || c > 0x7E) return CONTENT_TYPE_UNKNOWN;
|
||||
}
|
||||
return CONTENT_TYPE_TEXT;
|
||||
}
|
||||
|
||||
/* What a file's extension implies its content should be. UNKNOWN means
|
||||
* "no expectation" (e.g. fonts:README.md's sibling files, or anything
|
||||
* outside this project's four known shapes) -- process_file() skips the
|
||||
* cross-check entirely in that case, never warns on an extension it has
|
||||
* no opinion about. */
|
||||
static content_type_t expected_type_from_ext(const char *fpath) {
|
||||
size_t n = strlen(fpath);
|
||||
if (n >= 4 && strcmp(fpath + n - 4, ".der") == 0) return CONTENT_TYPE_DER;
|
||||
if (n >= 4 && strcmp(fpath + n - 4, ".ttf") == 0) return CONTENT_TYPE_TTF;
|
||||
if (n >= 4 && strcmp(fpath + n - 4, ".4th") == 0) return CONTENT_TYPE_TEXT;
|
||||
if (n >= 3 && strcmp(fpath + n - 3, ".md") == 0) return CONTENT_TYPE_TEXT;
|
||||
return CONTENT_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build the colon-separated capsule name from a relative path.
|
||||
* Returns 1 on success, 0 if the name would exceed CAPSULE_NAME_MAX-1 bytes.
|
||||
@@ -436,6 +505,20 @@ static int process_file(const char *fpath, const struct stat *sb,
|
||||
}
|
||||
}
|
||||
|
||||
/* Magic-number content-type cross-check (FABRIC-3.md §I.4) --
|
||||
* WARN-only, never refuses; UNKNOWN on either side means "no
|
||||
* expectation," not a mismatch. */
|
||||
{
|
||||
content_type_t want = expected_type_from_ext(fpath);
|
||||
content_type_t got = detect_content_type(data, (size_t)size);
|
||||
if (want != CONTENT_TYPE_UNKNOWN && got != CONTENT_TYPE_UNKNOWN && want != got) {
|
||||
fprintf(stderr,
|
||||
"mkcapsule: WARNING: %s: content doesn't match its extension "
|
||||
"(expected %s, detected %s)\n",
|
||||
fpath, content_type_name(want), content_type_name(got));
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill entry */
|
||||
CapsuleEntry *e = &capsules[capsule_count];
|
||||
snprintf(e->path, MAX_PATH_LEN, "%s", fpath);
|
||||
|
||||
Reference in New Issue
Block a user