Repository navigation
feat(gateway): wire device-signed (#236) evidence through #52 verifier to settlement — ready but gated (SEAM-2) - #244
Merged
Conversation
LamaSu
enabled auto-merge
July 11, 2026 19:29
…ism) The ready-but-gated core: verifyDeviceSignedEvidence (registered-signer #47 -> Ed25519 verify), the composite gate (machine.execution_log #52 verifierStatus live AND SEAM2_DEVICE_EVIDENCE_SETTLEMENT flag, both default OFF), and resolveSettlementEvidence (fails closed to the gateway anchor). Reads the #52 verifierStatus, never flips it — #233 stays stubbed. implementer-seam2
…hs (gated) Path 1 (operator-relay POST /api/operator/evidence): capture the node's REAL device Ed25519 signature + real bundleHash instead of the 'operator-relay-auto' placeholder. Stored tier stays 0 on purpose — unverified evidence supports only the tier-0 floor; the resume-settlement '?? latestBundle.assuranceTier' fallback must not escalate release tier from an unverified claim (fails closed). Path 2 (paid-job-flow /complete): resolveSettlementEvidence chooses the anchor. Gate CLOSED by default (#52 verifierStatus stub AND SEAM2 flag unset) => gateway fallback, byte-identical to before. Gate open => device hash+signature anchors, after verifying against the kernel's registered signer. Fails closed. Adds registeredSignerInputFromColumns helper. #233/verifierStatus untouched. implementer-seam2
…ation Unit: gate holds (real stubbed #52 + flag set => still closed), gate untouched (#52-#55 verifierStatus stub), wiring correct (device sig -> settlement anchor, mocked + REAL tweetnacl Ed25519), fail-closed (wrong key/tamper/unregistered), path-1 parser. Integration: operator-relay captures the real device signature (deviceSigned:true, stored value != placeholder, tier stays 0), placeholder fallback for non-bundle evidence. implementer-seam2
…hive sites StoredSignature (algorithm:string) -> the spec Signature union at the two archiveBundle calls (EvidenceBundle wants signer:Address, algorithm:ed25519| secp256k1). DB insert keeps StoredSignature (column type is algorithm:string). implementer-seam2
LamaSu
force-pushed
the
feat/seam2-device-signed-evidence-settlement
branch
from
July 11, 2026 23:02
d1e0c3f to
fcee88e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SEAM-2 — wire device-signed (#236) evidence into settlement. Ready but GATED.
The operator node already produces real Ed25519-signed evidence, but two gateway paths threw that signature away and anchored settlement on a fabricated gateway placeholder. This builds the mechanism to anchor on the device's own key, and leaves it OFF behind the still-stubbed #52 verifier — the money path is unchanged today.
The two inert paths (before)
operator-relay.tsPOST /api/operator/evidencestored the node's bundle inert:assuranceTier:0, a fabricatedbundleHash = \sha256-${bundleId}`, andkernelSignature.value:"operator-relay-auto"— the real Ed25519 signature inreq.body.evidence` was discarded.paid-job-flow.ts/completerebuilt the bundle and signed it with the ZERO address0x0000…0000/"gateway-auto-sign", then fed that todriveSettlement.The wiring built (
packages/gateway/src/services/device-evidence-settlement.ts, unit-tested)bundleHashwhen the pushed evidence carries a signed bundle (extractNodeSignedBundle); fall back to the placeholder for old / non-bundle nodes. The storedassuranceTierstays 0 on purpose — an unverified bundle "actually supports" only the tier-0 floor, andresume-settlement's?? latestBundle.assuranceTierfallback must not escalate the release tier from an unverified claim (fails closed).resolveSettlementEvidencechooses the settlement anchor. When a captured device bundle's signature verifies against the kernel's registered signer (normalizeRegisteredSignerchore: impl-alfa-2 post-cascade lockfile + release-please health audit #47 → tweetnacl Ed25519), settlement anchors on the device's hash + signature; otherwise it falls back to the gateway anchor. Threaded through the stored bundle, the IPFS archive, the oracle verify hash, anddriveSettlement.WHAT STAYED GATED (the safety gate — verified, not flipped)
packages/spec/is UNTOUCHED —git diff --statshows zero spec changes.machine.execution_log(feat: PLR adapter Phase 1 — @pcc/adapter-pylabrobot + Python sidecar + OT-2 profile (impl-uniform) #52) and feat: 4-level federation Phase 1 — @pcc/dht-core + @pcc/federation + CRDTs (impl-fed) #53–feat: PLR backend-author registry + EIP-712 signing client + kill-switch (impl-zulu) #55 remainverifierStatus:"stub"; the oracle binding still fails closed (met:false). spec: [GATED DRAFT] flip #47/#52 verifierStatus stub->live (machine-log settlement) #233 is not flipped.deviceEvidenceSettlementEnabled()=machineLogVerifierLive()(reads the real feat: PLR adapter Phase 1 — @pcc/adapter-pylabrobot + Python sidecar + OT-2 profile (impl-uniform) #52verifierStatus, currently"stub"→ false) ANDSEAM2_DEVICE_EVIDENCE_SETTLEMENTflag (default unset). Both default false, soresolveSettlementEvidencealways returns the gateway fallback today —/completebehavior is byte-identical. A unit test asserts the gate stays closed even with the flag set, because feat: PLR adapter Phase 1 — @pcc/adapter-pylabrobot + Python sidecar + OT-2 profile (impl-uniform) #52 is stubbed.SEAM-2 is ready-but-gated pending a real device on deployed infra. The post-deploy live proof is the harness
rehearse-loop.mjsA6 rehearsal — that flips the gate once a real device clears #52. No code change needed to turn it on.Tests
device-evidence-settlement.test.ts(23): gate holds (real stubbed feat: PLR adapter Phase 1 — @pcc/adapter-pylabrobot + Python sidecar + OT-2 profile (impl-uniform) #52 + flag set → still closed; valid device bundle → gateway fallback), gate untouched (feat: PLR adapter Phase 1 — @pcc/adapter-pylabrobot + Python sidecar + OT-2 profile (impl-uniform) #52–feat: PLR backend-author registry + EIP-712 signing client + kill-switch (impl-zulu) #55verifierStatusstub), wiring correct (device sig → device anchor, mocked verifier and real tweetnacl Ed25519 roundtrip), fails closed (wrong key / tampered hash / unregistered / non-ed25519 signer), path-1 parser,registeredSignerInputFromColumns.operator-relay.test.ts(+5): the route captures the real device signature end-to-end (deviceSigned:true, stored value ≠ placeholder, tier stays 0), placeholder fallback for non-bundle evidence.Verification (own worktree)
pnpm --filter @pcc/gateway test: 2063 passed, 6 skipped. The 1 failing test + 2 failing suites are pre-existing, environment-only (a WindowsC:\C:\…status.tsdouble-drive path bug that passes on Linux CI; unbuilt@pcc/verifier/dist/capture/*subpaths) — none touch evidence/settlement.pnpm --filter @pcc/gateway typecheck: exit 0, 0 errors (deps built).🤖 Generated with Claude Code