stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs - #12

Merged
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer
Jul 5, 2026
Merged

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs#12
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer

Conversation

@HarryR

Copy link
Copy Markdown
Collaborator

Splits deployment-config generation out of the on-instance stage1 binary into a separate host-side deploy tool, and extracts the wire types + ed25519 signer into shared crates so "what we emit" and "what we verify" cannot drift. Repos stay fully separate (stage0 keeps its own config.rs/sig.rs).

New crates

  • metadata - the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig, UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1 verifier and the deploy emitter both depend on it (one source of truth). Carries the JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
  • ed25519-sign - the ed25519 wire primitive: sign_payload + verify + sha256_hex. Extracted from mkuki/sign.rs (mkuki re-exports it as mkuki::sign) with stage1's sig.rs verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
  • deploy - lockboot-deploy create / validate / modify. create signs (or hashes) the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. validate checks a doc via the shared validate(); modify adds/removes mirror base URLs.

Slimmed

  • stage1 bin: on-instance runtime only. Removed make_config*/emit_config and the --make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses the metadata + ed25519-sign crates. Runtime behavior unchanged.
  • Makefile: exclude the host-only deploy tool from the payload-target cross-build.

Verification

  • Unit tests: ed25519-sign (2) + metadata (12) + deploy (3), all pass; both arches compile.
  • Smoke: create -> signed 2-mirror _stage1+_stage2 + signed args; validate clean; modify add/remove mirrors.
  • End-to-end: the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off cleanly against the deploy tool's OWN signed output - both ed25519 hops + signed remote args verified.

Note: deploy validate uses the authoritative Rust validate(). Wiring the jsonschema crate + a combined user-data schema + fixtures (to machine-check the shipped JSON schema) is left as a small follow-up.

Depends on nothing; the sibling stage0 fallback-URL change (lockboot/stage0#1) already merged.

Generated with Claude Code

HarryRand others added 2 commits July 5, 2026 11:23
…a + ed25519 libs
The stage1 runtime binary was dual-purpose (boot on-instance AND generate deployment
config via --make-config). Split config generation into a separate host-side tool so the
on-instance binary carries only runtime concerns, and share the wire types + signer so
"what we emit" and "what we verify" cannot drift.
- crates/metadata (new): the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig,
UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1
verifier and the deploy emitter both depend on it (one source of truth). Carries the
JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
- crates/ed25519-sign (new): the ed25519 wire primitive: sign_payload + verify + sha256_hex.
Extracted from mkuki/sign.rs (mkuki re-exports it as `mkuki::sign`) with stage1's sig.rs
verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
- crates/deploy (new): `lockboot-deploy` create/validate/modify. `create` signs (or hashes)
the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an
upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. `validate` checks a
doc via the shared validate(); `modify` adds/removes mirror base URLs.
- stage1 bin: slimmed to on-instance runtime only. Removed make_config*/emit_config and the
--make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses
the metadata + ed25519-sign crates. Runtime behavior unchanged.
- Makefile: exclude the host-only deploy tool from the payload-target cross-build.
Repos stay fully separate: stage0 keeps its own config.rs/sig.rs (minimal root-of-trust
audit surface); the shared crates live in the stage1 repo, consumed by its own crates.
Verified: ed25519-sign (2) + metadata (12) + deploy (3) unit tests pass; both arches
compile; the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off
cleanly against the deploy tool's OWN signed output: both ed25519 hops + signed remote args.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two hardening fixes from the boot-path security review:
- Install a panic hook that routes any panic through the same poweroff()
as a returned error. As PID 1 an unhandled panic would abort into a
kernel panic and skip the log-drain wait; now every failure (error or
panic) converges on one shutdown path so its logs reach the serial
console before power-off.
- download_binary returns reqwest's owned Bytes instead of .to_vec(), and
download_first/admit_from/admit_payload thread Bytes through. This drops
a full-length copy of the stage2 payload (and the signature / signed-args
blobs). Bytes derefs to [u8], so admission and PCR 14 measurement see
identical bytes and are unchanged.
Verified: full chain boots and powers off cleanly in both sha256 and
ed25519 modes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@HarryR
HarryR merged commit 764b6f5 into mainJul 5, 2026
4 checks passed
@HarryR
HarryR deleted the feat-extract-metadata-signer branch July 5, 2026 16:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@HarryR
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs - #12

Merged
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer
Jul 5, 2026
Merged

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs#12
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer

Conversation

@HarryR

Copy link
Copy Markdown
Collaborator

Splits deployment-config generation out of the on-instance stage1 binary into a separate host-side deploy tool, and extracts the wire types + ed25519 signer into shared crates so "what we emit" and "what we verify" cannot drift. Repos stay fully separate (stage0 keeps its own config.rs/sig.rs).

New crates

  • metadata - the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig, UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1 verifier and the deploy emitter both depend on it (one source of truth). Carries the JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
  • ed25519-sign - the ed25519 wire primitive: sign_payload + verify + sha256_hex. Extracted from mkuki/sign.rs (mkuki re-exports it as mkuki::sign) with stage1's sig.rs verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
  • deploy - lockboot-deploy create / validate / modify. create signs (or hashes) the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. validate checks a doc via the shared validate(); modify adds/removes mirror base URLs.

Slimmed

  • stage1 bin: on-instance runtime only. Removed make_config*/emit_config and the --make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses the metadata + ed25519-sign crates. Runtime behavior unchanged.
  • Makefile: exclude the host-only deploy tool from the payload-target cross-build.

Verification

  • Unit tests: ed25519-sign (2) + metadata (12) + deploy (3), all pass; both arches compile.
  • Smoke: create -> signed 2-mirror _stage1+_stage2 + signed args; validate clean; modify add/remove mirrors.
  • End-to-end: the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off cleanly against the deploy tool's OWN signed output - both ed25519 hops + signed remote args verified.

Note: deploy validate uses the authoritative Rust validate(). Wiring the jsonschema crate + a combined user-data schema + fixtures (to machine-check the shipped JSON schema) is left as a small follow-up.

Depends on nothing; the sibling stage0 fallback-URL change (lockboot/stage0#1) already merged.

Generated with Claude Code

HarryRand others added 2 commits July 5, 2026 11:23
…a + ed25519 libs
The stage1 runtime binary was dual-purpose (boot on-instance AND generate deployment
config via --make-config). Split config generation into a separate host-side tool so the
on-instance binary carries only runtime concerns, and share the wire types + signer so
"what we emit" and "what we verify" cannot drift.
- crates/metadata (new): the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig,
UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1
verifier and the deploy emitter both depend on it (one source of truth). Carries the
JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
- crates/ed25519-sign (new): the ed25519 wire primitive: sign_payload + verify + sha256_hex.
Extracted from mkuki/sign.rs (mkuki re-exports it as `mkuki::sign`) with stage1's sig.rs
verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
- crates/deploy (new): `lockboot-deploy` create/validate/modify. `create` signs (or hashes)
the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an
upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. `validate` checks a
doc via the shared validate(); `modify` adds/removes mirror base URLs.
- stage1 bin: slimmed to on-instance runtime only. Removed make_config*/emit_config and the
--make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses
the metadata + ed25519-sign crates. Runtime behavior unchanged.
- Makefile: exclude the host-only deploy tool from the payload-target cross-build.
Repos stay fully separate: stage0 keeps its own config.rs/sig.rs (minimal root-of-trust
audit surface); the shared crates live in the stage1 repo, consumed by its own crates.
Verified: ed25519-sign (2) + metadata (12) + deploy (3) unit tests pass; both arches
compile; the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off
cleanly against the deploy tool's OWN signed output: both ed25519 hops + signed remote args.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two hardening fixes from the boot-path security review:
- Install a panic hook that routes any panic through the same poweroff()
as a returned error. As PID 1 an unhandled panic would abort into a
kernel panic and skip the log-drain wait; now every failure (error or
panic) converges on one shutdown path so its logs reach the serial
console before power-off.
- download_binary returns reqwest's owned Bytes instead of .to_vec(), and
download_first/admit_from/admit_payload thread Bytes through. This drops
a full-length copy of the stage2 payload (and the signature / signed-args
blobs). Bytes derefs to [u8], so admission and PCR 14 measurement see
identical bytes and are unchanged.
Verified: full chain boots and powers off cleanly in both sha256 and
ed25519 modes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@HarryR
HarryR merged commit 764b6f5 into mainJul 5, 2026
4 checks passed
@HarryR
HarryR deleted the feat-extract-metadata-signer branch July 5, 2026 16:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@HarryR
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs - #12

Merged
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer
Jul 5, 2026
Merged

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs#12
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer

Conversation

@HarryR

Copy link
Copy Markdown
Collaborator

Splits deployment-config generation out of the on-instance stage1 binary into a separate host-side deploy tool, and extracts the wire types + ed25519 signer into shared crates so "what we emit" and "what we verify" cannot drift. Repos stay fully separate (stage0 keeps its own config.rs/sig.rs).

New crates

  • metadata - the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig, UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1 verifier and the deploy emitter both depend on it (one source of truth). Carries the JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
  • ed25519-sign - the ed25519 wire primitive: sign_payload + verify + sha256_hex. Extracted from mkuki/sign.rs (mkuki re-exports it as mkuki::sign) with stage1's sig.rs verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
  • deploy - lockboot-deploy create / validate / modify. create signs (or hashes) the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. validate checks a doc via the shared validate(); modify adds/removes mirror base URLs.

Slimmed

  • stage1 bin: on-instance runtime only. Removed make_config*/emit_config and the --make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses the metadata + ed25519-sign crates. Runtime behavior unchanged.
  • Makefile: exclude the host-only deploy tool from the payload-target cross-build.

Verification

  • Unit tests: ed25519-sign (2) + metadata (12) + deploy (3), all pass; both arches compile.
  • Smoke: create -> signed 2-mirror _stage1+_stage2 + signed args; validate clean; modify add/remove mirrors.
  • End-to-end: the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off cleanly against the deploy tool's OWN signed output - both ed25519 hops + signed remote args verified.

Note: deploy validate uses the authoritative Rust validate(). Wiring the jsonschema crate + a combined user-data schema + fixtures (to machine-check the shipped JSON schema) is left as a small follow-up.

Depends on nothing; the sibling stage0 fallback-URL change (lockboot/stage0#1) already merged.

Generated with Claude Code

HarryRand others added 2 commits July 5, 2026 11:23
…a + ed25519 libs
The stage1 runtime binary was dual-purpose (boot on-instance AND generate deployment
config via --make-config). Split config generation into a separate host-side tool so the
on-instance binary carries only runtime concerns, and share the wire types + signer so
"what we emit" and "what we verify" cannot drift.
- crates/metadata (new): the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig,
UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1
verifier and the deploy emitter both depend on it (one source of truth). Carries the
JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
- crates/ed25519-sign (new): the ed25519 wire primitive: sign_payload + verify + sha256_hex.
Extracted from mkuki/sign.rs (mkuki re-exports it as `mkuki::sign`) with stage1's sig.rs
verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
- crates/deploy (new): `lockboot-deploy` create/validate/modify. `create` signs (or hashes)
the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an
upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. `validate` checks a
doc via the shared validate(); `modify` adds/removes mirror base URLs.
- stage1 bin: slimmed to on-instance runtime only. Removed make_config*/emit_config and the
--make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses
the metadata + ed25519-sign crates. Runtime behavior unchanged.
- Makefile: exclude the host-only deploy tool from the payload-target cross-build.
Repos stay fully separate: stage0 keeps its own config.rs/sig.rs (minimal root-of-trust
audit surface); the shared crates live in the stage1 repo, consumed by its own crates.
Verified: ed25519-sign (2) + metadata (12) + deploy (3) unit tests pass; both arches
compile; the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off
cleanly against the deploy tool's OWN signed output: both ed25519 hops + signed remote args.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two hardening fixes from the boot-path security review:
- Install a panic hook that routes any panic through the same poweroff()
as a returned error. As PID 1 an unhandled panic would abort into a
kernel panic and skip the log-drain wait; now every failure (error or
panic) converges on one shutdown path so its logs reach the serial
console before power-off.
- download_binary returns reqwest's owned Bytes instead of .to_vec(), and
download_first/admit_from/admit_payload thread Bytes through. This drops
a full-length copy of the stage2 payload (and the signature / signed-args
blobs). Bytes derefs to [u8], so admission and PCR 14 measurement see
identical bytes and are unchanged.
Verified: full chain boots and powers off cleanly in both sha256 and
ed25519 modes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@HarryR
HarryR merged commit 764b6f5 into mainJul 5, 2026
4 checks passed
@HarryR
HarryR deleted the feat-extract-metadata-signer branch July 5, 2026 16:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@HarryR
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs - #12

Merged
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer
Jul 5, 2026
Merged

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs#12
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer

Conversation

@HarryR

Copy link
Copy Markdown
Collaborator

Splits deployment-config generation out of the on-instance stage1 binary into a separate host-side deploy tool, and extracts the wire types + ed25519 signer into shared crates so "what we emit" and "what we verify" cannot drift. Repos stay fully separate (stage0 keeps its own config.rs/sig.rs).

New crates

  • metadata - the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig, UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1 verifier and the deploy emitter both depend on it (one source of truth). Carries the JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
  • ed25519-sign - the ed25519 wire primitive: sign_payload + verify + sha256_hex. Extracted from mkuki/sign.rs (mkuki re-exports it as mkuki::sign) with stage1's sig.rs verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
  • deploy - lockboot-deploy create / validate / modify. create signs (or hashes) the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. validate checks a doc via the shared validate(); modify adds/removes mirror base URLs.

Slimmed

  • stage1 bin: on-instance runtime only. Removed make_config*/emit_config and the --make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses the metadata + ed25519-sign crates. Runtime behavior unchanged.
  • Makefile: exclude the host-only deploy tool from the payload-target cross-build.

Verification

  • Unit tests: ed25519-sign (2) + metadata (12) + deploy (3), all pass; both arches compile.
  • Smoke: create -> signed 2-mirror _stage1+_stage2 + signed args; validate clean; modify add/remove mirrors.
  • End-to-end: the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off cleanly against the deploy tool's OWN signed output - both ed25519 hops + signed remote args verified.

Note: deploy validate uses the authoritative Rust validate(). Wiring the jsonschema crate + a combined user-data schema + fixtures (to machine-check the shipped JSON schema) is left as a small follow-up.

Depends on nothing; the sibling stage0 fallback-URL change (lockboot/stage0#1) already merged.

Generated with Claude Code

HarryRand others added 2 commits July 5, 2026 11:23
…a + ed25519 libs
The stage1 runtime binary was dual-purpose (boot on-instance AND generate deployment
config via --make-config). Split config generation into a separate host-side tool so the
on-instance binary carries only runtime concerns, and share the wire types + signer so
"what we emit" and "what we verify" cannot drift.
- crates/metadata (new): the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig,
UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1
verifier and the deploy emitter both depend on it (one source of truth). Carries the
JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
- crates/ed25519-sign (new): the ed25519 wire primitive: sign_payload + verify + sha256_hex.
Extracted from mkuki/sign.rs (mkuki re-exports it as `mkuki::sign`) with stage1's sig.rs
verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
- crates/deploy (new): `lockboot-deploy` create/validate/modify. `create` signs (or hashes)
the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an
upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. `validate` checks a
doc via the shared validate(); `modify` adds/removes mirror base URLs.
- stage1 bin: slimmed to on-instance runtime only. Removed make_config*/emit_config and the
--make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses
the metadata + ed25519-sign crates. Runtime behavior unchanged.
- Makefile: exclude the host-only deploy tool from the payload-target cross-build.
Repos stay fully separate: stage0 keeps its own config.rs/sig.rs (minimal root-of-trust
audit surface); the shared crates live in the stage1 repo, consumed by its own crates.
Verified: ed25519-sign (2) + metadata (12) + deploy (3) unit tests pass; both arches
compile; the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off
cleanly against the deploy tool's OWN signed output: both ed25519 hops + signed remote args.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two hardening fixes from the boot-path security review:
- Install a panic hook that routes any panic through the same poweroff()
as a returned error. As PID 1 an unhandled panic would abort into a
kernel panic and skip the log-drain wait; now every failure (error or
panic) converges on one shutdown path so its logs reach the serial
console before power-off.
- download_binary returns reqwest's owned Bytes instead of .to_vec(), and
download_first/admit_from/admit_payload thread Bytes through. This drops
a full-length copy of the stage2 payload (and the signature / signed-args
blobs). Bytes derefs to [u8], so admission and PCR 14 measurement see
identical bytes and are unchanged.
Verified: full chain boots and powers off cleanly in both sha256 and
ed25519 modes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@HarryR
HarryR merged commit 764b6f5 into mainJul 5, 2026
4 checks passed
@HarryR
HarryR deleted the feat-extract-metadata-signer branch July 5, 2026 16:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@HarryR
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs - #12

Merged
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer
Jul 5, 2026
Merged

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs#12
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer

Conversation

@HarryR

Copy link
Copy Markdown
Collaborator

Splits deployment-config generation out of the on-instance stage1 binary into a separate host-side deploy tool, and extracts the wire types + ed25519 signer into shared crates so "what we emit" and "what we verify" cannot drift. Repos stay fully separate (stage0 keeps its own config.rs/sig.rs).

New crates

  • metadata - the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig, UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1 verifier and the deploy emitter both depend on it (one source of truth). Carries the JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
  • ed25519-sign - the ed25519 wire primitive: sign_payload + verify + sha256_hex. Extracted from mkuki/sign.rs (mkuki re-exports it as mkuki::sign) with stage1's sig.rs verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
  • deploy - lockboot-deploy create / validate / modify. create signs (or hashes) the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. validate checks a doc via the shared validate(); modify adds/removes mirror base URLs.

Slimmed

  • stage1 bin: on-instance runtime only. Removed make_config*/emit_config and the --make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses the metadata + ed25519-sign crates. Runtime behavior unchanged.
  • Makefile: exclude the host-only deploy tool from the payload-target cross-build.

Verification

  • Unit tests: ed25519-sign (2) + metadata (12) + deploy (3), all pass; both arches compile.
  • Smoke: create -> signed 2-mirror _stage1+_stage2 + signed args; validate clean; modify add/remove mirrors.
  • End-to-end: the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off cleanly against the deploy tool's OWN signed output - both ed25519 hops + signed remote args verified.

Note: deploy validate uses the authoritative Rust validate(). Wiring the jsonschema crate + a combined user-data schema + fixtures (to machine-check the shipped JSON schema) is left as a small follow-up.

Depends on nothing; the sibling stage0 fallback-URL change (lockboot/stage0#1) already merged.

Generated with Claude Code

HarryRand others added 2 commits July 5, 2026 11:23
…a + ed25519 libs
The stage1 runtime binary was dual-purpose (boot on-instance AND generate deployment
config via --make-config). Split config generation into a separate host-side tool so the
on-instance binary carries only runtime concerns, and share the wire types + signer so
"what we emit" and "what we verify" cannot drift.
- crates/metadata (new): the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig,
UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1
verifier and the deploy emitter both depend on it (one source of truth). Carries the
JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
- crates/ed25519-sign (new): the ed25519 wire primitive: sign_payload + verify + sha256_hex.
Extracted from mkuki/sign.rs (mkuki re-exports it as `mkuki::sign`) with stage1's sig.rs
verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
- crates/deploy (new): `lockboot-deploy` create/validate/modify. `create` signs (or hashes)
the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an
upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. `validate` checks a
doc via the shared validate(); `modify` adds/removes mirror base URLs.
- stage1 bin: slimmed to on-instance runtime only. Removed make_config*/emit_config and the
--make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses
the metadata + ed25519-sign crates. Runtime behavior unchanged.
- Makefile: exclude the host-only deploy tool from the payload-target cross-build.
Repos stay fully separate: stage0 keeps its own config.rs/sig.rs (minimal root-of-trust
audit surface); the shared crates live in the stage1 repo, consumed by its own crates.
Verified: ed25519-sign (2) + metadata (12) + deploy (3) unit tests pass; both arches
compile; the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off
cleanly against the deploy tool's OWN signed output: both ed25519 hops + signed remote args.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two hardening fixes from the boot-path security review:
- Install a panic hook that routes any panic through the same poweroff()
as a returned error. As PID 1 an unhandled panic would abort into a
kernel panic and skip the log-drain wait; now every failure (error or
panic) converges on one shutdown path so its logs reach the serial
console before power-off.
- download_binary returns reqwest's owned Bytes instead of .to_vec(), and
download_first/admit_from/admit_payload thread Bytes through. This drops
a full-length copy of the stage2 payload (and the signature / signed-args
blobs). Bytes derefs to [u8], so admission and PCR 14 measurement see
identical bytes and are unchanged.
Verified: full chain boots and powers off cleanly in both sha256 and
ed25519 modes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@HarryR
HarryR merged commit 764b6f5 into mainJul 5, 2026
4 checks passed
@HarryR
HarryR deleted the feat-extract-metadata-signer branch July 5, 2026 16:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@HarryR
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs - #12

Merged
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer
Jul 5, 2026
Merged

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs#12
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer

Conversation

@HarryR

Copy link
Copy Markdown
Collaborator

Splits deployment-config generation out of the on-instance stage1 binary into a separate host-side deploy tool, and extracts the wire types + ed25519 signer into shared crates so "what we emit" and "what we verify" cannot drift. Repos stay fully separate (stage0 keeps its own config.rs/sig.rs).

New crates

  • metadata - the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig, UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1 verifier and the deploy emitter both depend on it (one source of truth). Carries the JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
  • ed25519-sign - the ed25519 wire primitive: sign_payload + verify + sha256_hex. Extracted from mkuki/sign.rs (mkuki re-exports it as mkuki::sign) with stage1's sig.rs verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
  • deploy - lockboot-deploy create / validate / modify. create signs (or hashes) the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. validate checks a doc via the shared validate(); modify adds/removes mirror base URLs.

Slimmed

  • stage1 bin: on-instance runtime only. Removed make_config*/emit_config and the --make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses the metadata + ed25519-sign crates. Runtime behavior unchanged.
  • Makefile: exclude the host-only deploy tool from the payload-target cross-build.

Verification

  • Unit tests: ed25519-sign (2) + metadata (12) + deploy (3), all pass; both arches compile.
  • Smoke: create -> signed 2-mirror _stage1+_stage2 + signed args; validate clean; modify add/remove mirrors.
  • End-to-end: the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off cleanly against the deploy tool's OWN signed output - both ed25519 hops + signed remote args verified.

Note: deploy validate uses the authoritative Rust validate(). Wiring the jsonschema crate + a combined user-data schema + fixtures (to machine-check the shipped JSON schema) is left as a small follow-up.

Depends on nothing; the sibling stage0 fallback-URL change (lockboot/stage0#1) already merged.

Generated with Claude Code

HarryRand others added 2 commits July 5, 2026 11:23
…a + ed25519 libs
The stage1 runtime binary was dual-purpose (boot on-instance AND generate deployment
config via --make-config). Split config generation into a separate host-side tool so the
on-instance binary carries only runtime concerns, and share the wire types + signer so
"what we emit" and "what we verify" cannot drift.
- crates/metadata (new): the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig,
UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1
verifier and the deploy emitter both depend on it (one source of truth). Carries the
JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
- crates/ed25519-sign (new): the ed25519 wire primitive: sign_payload + verify + sha256_hex.
Extracted from mkuki/sign.rs (mkuki re-exports it as `mkuki::sign`) with stage1's sig.rs
verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
- crates/deploy (new): `lockboot-deploy` create/validate/modify. `create` signs (or hashes)
the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an
upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. `validate` checks a
doc via the shared validate(); `modify` adds/removes mirror base URLs.
- stage1 bin: slimmed to on-instance runtime only. Removed make_config*/emit_config and the
--make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses
the metadata + ed25519-sign crates. Runtime behavior unchanged.
- Makefile: exclude the host-only deploy tool from the payload-target cross-build.
Repos stay fully separate: stage0 keeps its own config.rs/sig.rs (minimal root-of-trust
audit surface); the shared crates live in the stage1 repo, consumed by its own crates.
Verified: ed25519-sign (2) + metadata (12) + deploy (3) unit tests pass; both arches
compile; the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off
cleanly against the deploy tool's OWN signed output: both ed25519 hops + signed remote args.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two hardening fixes from the boot-path security review:
- Install a panic hook that routes any panic through the same poweroff()
as a returned error. As PID 1 an unhandled panic would abort into a
kernel panic and skip the log-drain wait; now every failure (error or
panic) converges on one shutdown path so its logs reach the serial
console before power-off.
- download_binary returns reqwest's owned Bytes instead of .to_vec(), and
download_first/admit_from/admit_payload thread Bytes through. This drops
a full-length copy of the stage2 payload (and the signature / signed-args
blobs). Bytes derefs to [u8], so admission and PCR 14 measurement see
identical bytes and are unchanged.
Verified: full chain boots and powers off cleanly in both sha256 and
ed25519 modes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@HarryR
HarryR merged commit 764b6f5 into mainJul 5, 2026
4 checks passed
@HarryR
HarryR deleted the feat-extract-metadata-signer branch July 5, 2026 16:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@HarryR
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs - #12

Merged
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer
Jul 5, 2026
Merged

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs#12
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer

Conversation

@HarryR

Copy link
Copy Markdown
Collaborator

Splits deployment-config generation out of the on-instance stage1 binary into a separate host-side deploy tool, and extracts the wire types + ed25519 signer into shared crates so "what we emit" and "what we verify" cannot drift. Repos stay fully separate (stage0 keeps its own config.rs/sig.rs).

New crates

  • metadata - the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig, UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1 verifier and the deploy emitter both depend on it (one source of truth). Carries the JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
  • ed25519-sign - the ed25519 wire primitive: sign_payload + verify + sha256_hex. Extracted from mkuki/sign.rs (mkuki re-exports it as mkuki::sign) with stage1's sig.rs verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
  • deploy - lockboot-deploy create / validate / modify. create signs (or hashes) the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. validate checks a doc via the shared validate(); modify adds/removes mirror base URLs.

Slimmed

  • stage1 bin: on-instance runtime only. Removed make_config*/emit_config and the --make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses the metadata + ed25519-sign crates. Runtime behavior unchanged.
  • Makefile: exclude the host-only deploy tool from the payload-target cross-build.

Verification

  • Unit tests: ed25519-sign (2) + metadata (12) + deploy (3), all pass; both arches compile.
  • Smoke: create -> signed 2-mirror _stage1+_stage2 + signed args; validate clean; modify add/remove mirrors.
  • End-to-end: the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off cleanly against the deploy tool's OWN signed output - both ed25519 hops + signed remote args verified.

Note: deploy validate uses the authoritative Rust validate(). Wiring the jsonschema crate + a combined user-data schema + fixtures (to machine-check the shipped JSON schema) is left as a small follow-up.

Depends on nothing; the sibling stage0 fallback-URL change (lockboot/stage0#1) already merged.

Generated with Claude Code

HarryRand others added 2 commits July 5, 2026 11:23
…a + ed25519 libs
The stage1 runtime binary was dual-purpose (boot on-instance AND generate deployment
config via --make-config). Split config generation into a separate host-side tool so the
on-instance binary carries only runtime concerns, and share the wire types + signer so
"what we emit" and "what we verify" cannot drift.
- crates/metadata (new): the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig,
UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1
verifier and the deploy emitter both depend on it (one source of truth). Carries the
JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
- crates/ed25519-sign (new): the ed25519 wire primitive: sign_payload + verify + sha256_hex.
Extracted from mkuki/sign.rs (mkuki re-exports it as `mkuki::sign`) with stage1's sig.rs
verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
- crates/deploy (new): `lockboot-deploy` create/validate/modify. `create` signs (or hashes)
the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an
upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. `validate` checks a
doc via the shared validate(); `modify` adds/removes mirror base URLs.
- stage1 bin: slimmed to on-instance runtime only. Removed make_config*/emit_config and the
--make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses
the metadata + ed25519-sign crates. Runtime behavior unchanged.
- Makefile: exclude the host-only deploy tool from the payload-target cross-build.
Repos stay fully separate: stage0 keeps its own config.rs/sig.rs (minimal root-of-trust
audit surface); the shared crates live in the stage1 repo, consumed by its own crates.
Verified: ed25519-sign (2) + metadata (12) + deploy (3) unit tests pass; both arches
compile; the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off
cleanly against the deploy tool's OWN signed output: both ed25519 hops + signed remote args.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two hardening fixes from the boot-path security review:
- Install a panic hook that routes any panic through the same poweroff()
as a returned error. As PID 1 an unhandled panic would abort into a
kernel panic and skip the log-drain wait; now every failure (error or
panic) converges on one shutdown path so its logs reach the serial
console before power-off.
- download_binary returns reqwest's owned Bytes instead of .to_vec(), and
download_first/admit_from/admit_payload thread Bytes through. This drops
a full-length copy of the stage2 payload (and the signature / signed-args
blobs). Bytes derefs to [u8], so admission and PCR 14 measurement see
identical bytes and are unchanged.
Verified: full chain boots and powers off cleanly in both sha256 and
ed25519 modes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@HarryR
HarryR merged commit 764b6f5 into mainJul 5, 2026
4 checks passed
@HarryR
HarryR deleted the feat-extract-metadata-signer branch July 5, 2026 16:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@HarryR
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs - #12

Merged
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer
Jul 5, 2026
Merged

stage1: split config-gen into a deploy tool; extract shared metadata + ed25519 libs#12
HarryR merged 2 commits into
mainfrom
feat-extract-metadata-signer

Conversation

@HarryR

Copy link
Copy Markdown
Collaborator

Splits deployment-config generation out of the on-instance stage1 binary into a separate host-side deploy tool, and extracts the wire types + ed25519 signer into shared crates so "what we emit" and "what we verify" cannot drift. Repos stay fully separate (stage0 keeps its own config.rs/sig.rs).

New crates

  • metadata - the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig, UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1 verifier and the deploy emitter both depend on it (one source of truth). Carries the JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
  • ed25519-sign - the ed25519 wire primitive: sign_payload + verify + sha256_hex. Extracted from mkuki/sign.rs (mkuki re-exports it as mkuki::sign) with stage1's sig.rs verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
  • deploy - lockboot-deploy create / validate / modify. create signs (or hashes) the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. validate checks a doc via the shared validate(); modify adds/removes mirror base URLs.

Slimmed

  • stage1 bin: on-instance runtime only. Removed make_config*/emit_config and the --make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses the metadata + ed25519-sign crates. Runtime behavior unchanged.
  • Makefile: exclude the host-only deploy tool from the payload-target cross-build.

Verification

  • Unit tests: ed25519-sign (2) + metadata (12) + deploy (3), all pass; both arches compile.
  • Smoke: create -> signed 2-mirror _stage1+_stage2 + signed args; validate clean; modify add/remove mirrors.
  • End-to-end: the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off cleanly against the deploy tool's OWN signed output - both ed25519 hops + signed remote args verified.

Note: deploy validate uses the authoritative Rust validate(). Wiring the jsonschema crate + a combined user-data schema + fixtures (to machine-check the shipped JSON schema) is left as a small follow-up.

Depends on nothing; the sibling stage0 fallback-URL change (lockboot/stage0#1) already merged.

Generated with Claude Code

HarryRand others added 2 commits July 5, 2026 11:23
…a + ed25519 libs
The stage1 runtime binary was dual-purpose (boot on-instance AND generate deployment
config via --make-config). Split config generation into a separate host-side tool so the
on-instance binary carries only runtime concerns, and share the wire types + signer so
"what we emit" and "what we verify" cannot drift.
- crates/metadata (new): the _stage1/_stage2 wire types (UrlList, ArchConfig, StageConfig,
UserData) + Verify + validate(Profile), extracted from the stage1 bin. The stage1
verifier and the deploy emitter both depend on it (one source of truth). Carries the
JSON schema + the validate() test table. Profile::Stage0 is http-only; Stage1 allows https.
- crates/ed25519-sign (new): the ed25519 wire primitive: sign_payload + verify + sha256_hex.
Extracted from mkuki/sign.rs (mkuki re-exports it as `mkuki::sign`) with stage1's sig.rs
verify folded in, so mkuki (sign), deploy (sign) and stage1 (verify) share one crate.
- crates/deploy (new): `lockboot-deploy` create/validate/modify. `create` signs (or hashes)
the UKI + stage2, composes mirror URL lists from repeated --base-url, and emits an
upload-ready dir + a merged user-data.json carrying _stage1 + _stage2. `validate` checks a
doc via the shared validate(); `modify` adds/removes mirror base URLs.
- stage1 bin: slimmed to on-instance runtime only. Removed make_config*/emit_config and the
--make-config* CLI; keeps --attest, --url/--file, PID1 boot, admission, measurement. Uses
the metadata + ed25519-sign crates. Runtime behavior unchanged.
- Makefile: exclude the host-only deploy tool from the payload-target cross-build.
Repos stay fully separate: stage0 keeps its own config.rs/sig.rs (minimal root-of-trust
audit surface); the shared crates live in the stage1 repo, consumed by its own crates.
Verified: ed25519-sign (2) + metadata (12) + deploy (3) unit tests pass; both arches
compile; the full chain (stage0 -> UKI -> stage1 -> example-stage2) boots and powers off
cleanly against the deploy tool's OWN signed output: both ed25519 hops + signed remote args.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two hardening fixes from the boot-path security review:
- Install a panic hook that routes any panic through the same poweroff()
as a returned error. As PID 1 an unhandled panic would abort into a
kernel panic and skip the log-drain wait; now every failure (error or
panic) converges on one shutdown path so its logs reach the serial
console before power-off.
- download_binary returns reqwest's owned Bytes instead of .to_vec(), and
download_first/admit_from/admit_payload thread Bytes through. This drops
a full-length copy of the stage2 payload (and the signature / signed-args
blobs). Bytes derefs to [u8], so admission and PCR 14 measurement see
identical bytes and are unchanged.
Verified: full chain boots and powers off cleanly in both sha256 and
ed25519 modes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@HarryR
HarryR merged commit 764b6f5 into mainJul 5, 2026
4 checks passed
@HarryR
HarryR deleted the feat-extract-metadata-signer branch July 5, 2026 16:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@HarryR