fix(gateway): serve simulated flag + ALCOA authentic leg + reconcile deviceType enum (detector gateway-side) - #243
Merged
Conversation
…ricated lives in #240 @pcc/spec)
…erify + detect fabrication implementer-gw-followups: the verification oracle fetches GET /api/evidence/:evidenceHash, re-hashes the RAW bytes to the committed hash (fail-closed), and reads events[] off the verified doc for its authenticity-of-origin floor (pcc-oracle PR #15). The gateway had NO by-hash serve path: the route treated the param as a jobId (every oracle fetch 404'd), and Path-B (gateway-synth) committed a hash whose preimage was discarded — unverifiable by construction. - evidence repo: findByHash (sha256:/0x/bare forms) + interface - evidence-envelope service: canonical envelope (spec canonicalize, events sorted by id, bundleHash/tenantId excluded) = the served bytes - GET /api/evidence/:param: hash-form params serve the canonical envelope verbatim (raw string body — byte-exact for the oracle re-hash); jobId behavior unchanged, hash-miss falls through - paid-job-flow Path-B: bundleHash now committed over that exact envelope (events inside -> flags inside), audit-trail binding preserved as auditTrailSha256 (+customEvidenceHash) INSIDE the envelope instead of a discarded preimage; kernelSignature deduped to one const - db schema: source $type widened with simulated?: boolean (JSON column already round-trips it; type-level honesty only) Path-A kernel bundles still commit hashBundle(events) (event-hash array) — raw-byte re-hash cannot match; oracle fails closed (safe). Structured re-verify is the flagged oracle-side ticket (handoff par.6). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ti8XbczbzSquGXzquvAgjp
…ge (fabrication-detection.test serve leg + schemas.test); ALCOA/settlement tests are #240's
…by the new test) implementer-gw-followups: the sila-instrument acceptance test failed on Spark — EvidenceEventTypeSchema had the SAME drift as deviceType: 27 hand-listed values vs 56 in the TS union. sila instrument_result, modbus sensor_data_summary, machine-log printer_log_captured / log_hash_chain_entry, batch + digital-workflow events all failed parse wherever the schema was enforced. EVIDENCE_EVENT_TYPES is now the single source of truth (TS union derived from it, z.enum over it), with a loop test pinning every value on both axes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ti8XbczbzSquGXzquvAgjp
LamaSu
force-pushed
the
fix/gateway-detector-followups
branch
from
July 12, 2026 00:52
9104e83 to
7ebe56d
Compare
…tract The isFabricated describe in schemas.test.ts (from ac117eb) asserted isFabricated(null)/isFabricated(undefined) === false — behavior of the null-safe LOCAL duplicate that was removed in the #240 reconciliation. The canonical predicate (#240 packages/spec/src/evidence/is-fabricated.ts) is typed for a non-null EvidenceEvent and reads event.source directly, so null input throws. This is a test-contract mismatch, NOT a gate weakening: every detector call site (ALCOA authentic leg, settlement gate, bundleHasFabricatedEvents) iterates a real events[] and never passes null; #240's is-fabricated.test.ts is the authoritative predicate coverage. Test now asserts the in-contract behavior (untagged event -> honest via optional chaining).
LamaSu
marked this pull request as ready for review
July 12, 2026 01:17
LamaSu
enabled auto-merge
July 12, 2026 01:17
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.
Detector gateway-side follow-ups (coord #312)
The oracle now refuses fabricated evidence (LamaSu/pcc-oracle #15: authenticity-of-origin floor reads
source.simulated===true || payload.mock===trueoff the hash-verified bundle →verified:falsefor tier≥1 → no attestation → no release). The adapter side (#238, merged) tags every mock/simulated event. This PR is the three gateway/spec follow-ups from the handoff doc (ai/research/evidence-oracle-fabrication-detection-handoff.md) so the flag actually reaches the oracle and the compliance report catches fabrication too.FIX 1 — Serve the flag (
GET /api/evidence/:hash) — the load-bearing oneBefore: the oracle fetches
GET /api/evidence/:evidenceHashand re-hashes the raw bytes to the committed hash, failing closed (evidence-checker.ts:137-151). The gateway had no by-hash path at all —routes/settlement.ts:225treated the param as a jobId (findByJob(hash)→ 404 for every oracle fetch), and nothing stored could ever re-hash to the committed value:paid-job-flow.ts) hashed an ad-hoc{jobId, auditTrail, ...}JSON and discarded the preimage — unverifiable by construction.hashBundle(events)(sorted event-hash array) — no bundle-shaped doc can raw-byte-match it (pre-existing hash-model conflict, flagged for the oracle owner, handoff §6).After:
repos.evidence.findByHash(acceptssha256:/0x/bare 64-hex;packages/db).services/evidence-envelope.ts— the canonical envelope (speccanonicalize, events sorted by id,bundleHash/tenantIdexcluded) = the exact bytes served.GET /api/evidence/:param— hash-form params serve the envelope verbatim (raw string body; byte-exact for the oracle re-hash). jobId behavior unchanged; hash-miss falls through.bundleHash = sha256(<canonical envelope>)— the served bytes verify, and the events (with any tags) ride inside. Audit-trail binding preserved asauditTrailSha256(+customEvidenceHash) inside the envelope — strictly better than the discarded preimage. AllbundleHashconsumers (driveSettlement, ZK commitment, EAS payload) treat it as an opaque digest — verified per call site.evidence_events.sourceis a drizzle JSON column (stringify/parse round-trip, nothing stripssimulated); no zod boundary in the repo layer;$typewidened for type honesty.Path-B finding (asked to "note what you find"): the gateway-synth events derive from the real tool-call audit trail (digital execution records), so tagging them
source.simulated:truewould be a false claim and is not done. They remain covered by their hardcoded zero-address signer (ALCOAoriginalfails + the oracle identity check fails). Caller-suppliedevidenceEventspass payloads through — a caller-markedpayload.mock:trueevent is detected end-to-end now.FIX 2 — ALCOA
authenticleg + settlement gate (defense-in-depth)A fabricated bundle with a REAL signing key scored ALCOA-clean (Spark-verified gap, handoff §5.0):
originaltests signature-origin, and Option C gives every device a real key. Now:computeAlcoahas an 11thauthenticleg — false when any eventisFabricated(shared predicate).originalkeeps its meaning; the §5.3 test asserts the exact contrast (original:true+authentic:falseon a mock-tagged bundle with a non-zero signer).authenticfeeds a critical finding →computeAssuranceScorehard-zeros (assurance-score.ts:148-154).SettlementService.processEvidence— release-path twin of the oracle floor: tier≥1 bundle with a fabricated event still archives + persists (honest, self-identifying record) but the on-chain evidence submit and auto-release are refused (result.error: simulated_evidence…). Tier 0 exempt, mirroring the oracle.FIX 3 — stale zod enums reconciled (real correctness bug, twice)
EvidenceSourceSchema.deviceType: 9 hand-listed values vs 26 in the TS union → sila/modbus/opentrons/instrument events failedEvidenceEventSchema.parsewherever enforced. Now derived fromEVIDENCE_DEVICE_TYPES(single source of truth intypes/evidence.ts).EvidenceEventTypeSchemahad the same drift (27 of 56 values —instrument_result,sensor_data_summary,printer_log_captured,log_hash_chain_entry, batch + digital-workflow events all failed parse). Same treatment:EVIDENCE_EVENT_TYPESsingle source; loop tests pin both axes.Shared predicate
isFabricated(e)exported once from@pcc/specbesideEvidenceEvent— strict=== trueon both legs (money-path) — called by the ALCOA leg and the settlement gate; mirrored by value inpcc-oracle/src/provenance.ts(no cross-repo dep by convention; field names are load-bearing).Deliberately deferred (out of scope)
The on-chain EAS v3
simulatedschema field / MilestoneEscrowV3 gate / new factory (handoff §3c) — heaviest, deploy-gated; to be done after the oracle detector (#15) proves out, with an owner-coordinated factory redeploy.Notes for reviewers
hashBundle(events)— oracle fails closed (safe, pre-existing). Structured re-verification (recompute event hashes →hashBundle) is the oracle-side ticket (handoff §6); the flags are inside the served bytes and inside each event hash, so it composes.mockVerificationin the gateway oracle-client still returnsverified:trueunconditionally — the oracle lane's companion fix (fix(gateway): make mock oracle honest — fail closed for paid tiers (S4) #234 per the oracle commit message); not touched here.ALCOA_WEIGHTSignores the new key (enforcement is via the critical finding), so scores for honest bundles are unchanged.Tests (DGX Spark, full-sync)
@pcc/specgreen (incl. per-value enum loops +isFabricatedsemantics + silainstrument_resultacceptance)@pcc/store192/192 (incl.findByHash)@pcc/gateway— see checks; newfabrication-detection.test.tscovers: served bytes re-hash to committed hash + carry the flag (all 3 hash forms + fallthrough), §5.3 ALCOA contrast + hard-zero, settlement gate refuse/exempt/persist matrix.Refs: LamaSu/pcc-oracle#15 · #238 ·
ai/research/evidence-oracle-fabrication-detection-handoff.md·ai/research/industry-pain-points/gateway-detector-followups-report.md🤖 Generated with Claude Code
https://claude.ai/code/session_01Ti8XbczbzSquGXzquvAgjp
⚠ Coordination: overlap with draft PR #240 (
fix/fabrication-detector-side)Discovered after opening this PR: #240 is a parallel detector-side lane (coord #312/#316) that also edits
compliance.facade.ts+settlement-service.tsand defines its ownisFabricated(atspec/src/evidence/is-fabricated.ts; mine is atspec/src/types/evidence.tsbeside theEvidenceEventtype, per the handoff §3d placement). The two will merge-conflict on those three areas — one implementation should win per area, imports re-pointed to ONE predicate.Disjoint (only in this PR): FIX 1 serve-by-hash (
routes/settlement.ts,evidence-envelope.ts,paid-job-flow.tsPath-B hash alignment,dbfindByHash) — the load-bearing piece the oracle fetch depends on — and FIX 3 enum reconciliation (EVIDENCE_DEVICE_TYPES+EVIDENCE_EVENT_TYPES). Only in #240: the kernel-side tier gate (evidence-emitter.tscheckTierRequirements, handoff §3d item 2 — deliberately out of this PR's scope).No conflict with #233 (evidence-vocab lane) — it touches only
spec/src/evidence/primitives.ts.