hub v0.13.0: DR recipe — assemble + store + view the secret-free reconstruction recipe
DR recipe slice (hub half), grounded in SPIKE-dr-recipe-2026-06-16. The hub
receives two additive dr_recipe halves on the existing report paths (agent
storage/guest/PBS on host-report; controller customer/apps on the controller
report), stores them PLAINTEXT in a DEDICATED dr_recipe table keyed by customer
(each half preserves the other), and AssembleDRRecipe stitches them into one
operator-readable recipe (ignore-unknown + version-skew tolerant).
View: a DR-recipe panel on the customer page + GET /customers/{id}/dr-recipe.json
download (operator-auth, no secrets to redact). Plaintext-at-rest is correct —
the recipe is the clean inverse of the retired infra-backup.
Tests: store round-trip (each half preserves the other), assemble-matches-golden,
ignore-unknown + version skew, partial halves, no-secrets sweep. Manifest tag
bumped to v0.13.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -559,6 +559,36 @@
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
{{if .HasDRRecipe}}
|
||||
<!-- DR recipe (secret-free reconstruction recipe) -->
|
||||
<section class="card">
|
||||
<h2>DR Recipe <span class="text-muted" style="font-size: 0.85em; font-weight: normal;">(secret-free reconstruction plan)</span></h2>
|
||||
<p class="text-muted" style="margin-top: 0;">
|
||||
The non-secret re-provision plan — guest sizing, drive inventory (durable-id → role → mount → intent),
|
||||
PVE storage defs, PBS coordinates, and app inventory + storage bindings. It complements escrow (keys)
|
||||
and PBS/restic (bytes): <strong>it contains no key, password, or token</strong>. Use it to rebuild the
|
||||
host/guest/storage scaffolding before the PBS bytes land.
|
||||
</p>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<span class="label">Storage / guest / PBS half (agent)</span>
|
||||
<span class="value">{{if .DRRecipeHasHost}}<span class="badge badge-ok">present</span>{{else}}<span class="badge badge-warn">awaiting host-report</span>{{end}}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">Customer / apps half (controller)</span>
|
||||
<span class="value">{{if .DRRecipeHasApps}}<span class="badge badge-ok">present</span>{{else}}<span class="badge badge-warn">awaiting controller report</span>{{end}}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">Last updated</span>
|
||||
<span class="value">{{if .DRRecipeUpdatedAt}}{{.DRRecipeUpdatedAt}}{{else}}—{{end}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 1rem;">
|
||||
<a href="/customers/{{.CustomerID}}/dr-recipe.json" class="btn" download>Download recipe (JSON)</a>
|
||||
</div>
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
<!-- Notifications -->
|
||||
<section class="card">
|
||||
<h2>Notifications</h2>
|
||||
|
||||
Reference in New Issue
Block a user