slice 10B: operator-signed destructive completion (offline key + signing CLI) (v0.16.0)
A destructive op runs ONLY on a pinned-key-verified, nonce-fresh, in-window, host-bound, durable-id-bound operator signature. New cmd/felhom-opsign signs canonical OpBlobs offline via ssh-keygen -Y sign (hardware-ready); the signing key is never in the hub or agent. New internal/signedjobs runner verifies each queued blob through the gate and only on all-pass runs the WipeExecutor, which re-resolves the DURABLE device id + re-inspects (8C) before mkfs — closing the 8C data-bearing-wipe pending_signature gap. New storage durable-device resolution; authz.CanonicalBlob promoted to production. Real-crypto tests assert valid executes and forged/replay/expired/retarget/non-pinned are rejected (executor never called). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,22 @@ type EnvelopeObserver interface {
|
||||
OnEnvelope(ctx context.Context, env *ControlEnvelope)
|
||||
}
|
||||
|
||||
// MultiObserver fans one envelope out to several observers in order (e.g. the desired-state
|
||||
// syncer + the signed-jobs runner). nil entries are skipped.
|
||||
func MultiObserver(observers ...EnvelopeObserver) EnvelopeObserver {
|
||||
return multiObserver(observers)
|
||||
}
|
||||
|
||||
type multiObserver []EnvelopeObserver
|
||||
|
||||
func (m multiObserver) OnEnvelope(ctx context.Context, env *ControlEnvelope) {
|
||||
for _, o := range m {
|
||||
if o != nil {
|
||||
o.OnEnvelope(ctx, env)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop is the agent's first daemon run loop: collect a host-report, POST it, adopt
|
||||
// the hub's cadence, repeat. It is resilient — a collect or report error is logged
|
||||
// and the loop continues (the data plane is independent of the agent; a hub outage
|
||||
|
||||
Reference in New Issue
Block a user