Repository navigation
evidence: prevent caller-backdated source-snapshot availability #529
Description
Scientific finding
#524 requires source-byte lineage that cannot be caller-backdated before Rubin numeric admission. #527 now owns receipt/source/content identity correctly, but its public SourceSnapshotReceiptV1::from_source_artifact(snapshot_id, source_artifact, available_at) still accepts an arbitrary AvailableTime value from the caller.
That means a source first observed after a historical KnowledgeCutoff can be wrapped in a newly minted Evidence receipt whose available_at is set before the cutoff. Canonical RFC3339 validation proves timestamp shape, not that TEPP actually observed the source at that time. The current receipt must therefore not be described as establishing authoritative availability by itself.
Required RED
Exercise the public creation/admission path with one immutable SourceArtifact whose trusted observation/ingestion clock is after the historical cutoff, while a caller attempts to supply an earlier AvailableTime. The Evidence-owned contract must make that backdating impossible or reject it before Analysis can admit the numeric rows.
Do not use private field mutation or a blacklist of timestamps.
Owner-correct repair direction
Introduce/reuse an Evidence/Temporal owner record that binds SourceArtifact identity to a trusted observation/ingestion clock before SourceSnapshotReceiptV1 is minted. The receipt constructor should consume that owner-controlled record/binding rather than a free AvailableTime parameter. Test clocks may be injectable only behind an explicit trusted boundary; production callers must not be able to choose historical availability arbitrarily.
Preserve distinctions among event/document/system/available time. Do not replace availability with source publication time, receipt creation time, or estimator_payload_sha256.
Acceptance
- source availability used by validation: bind Rubin executor inputs to immutable source snapshot digest #524 is derived from a trusted Evidence/Temporal owner contract, not a free receipt-constructor argument;
- caller backdating across a
KnowledgeCutofffails closed; - persisted wire reconstruction validates but does not self-authenticate arbitrary timestamps;
- feat(evidence): own immutable source snapshot receipts #527 rustdoc/PR/TRACEABILITY call the current clock field
recordedrather thanauthoritativeuntil this owner path lands; - no positive Rubin projection authority is granted before validation: bind Rubin executor inputs to immutable source snapshot digest #524 integration and independent Validation Evidence promotion.
Activity
seonghobae commentedon Sep 15, 2026
#529의 owner-path를 #527 branch에서 구현했습니다. 다만 hosted exact-head GREEN 전이므로 이 issue는 닫지 않습니다.
5548f124a656e88da70699c2507ecfc2f088ee16→ private wire DTO + canonical byte equality fix29450bd9a8837230a9ecbb4b7a9d2ffe249e5fba; 공개Deserialize우회도 제거했습니다.841c7cadece3a5a24653e1a7f17c2b9b05d27447에서 강화해SystemTime과AvailableTime을 같은 시각으로 강제하지 않고 별도 owner event로 분리했습니다.032b9d7db88f2f0b769fd7ef0a3a5a1916769570:SourceObservation::observe가 Evidence 경계 내부에서 system observation clock을 mint하고,SourceAvailability::make_available가 별도 Evidence-owned clock read로 availability를 mint합니다. production public API에는 historical timestamp 인자가 없습니다. availability가 observation보다 이르면 fail closed합니다.d7e0d7e423ac1d221c61bf2caa9bde43f8159b91에서 owner records를 export했고,261a3eaf1ad3f2776b280758a1d327336b73b55f에서 receipt가 freeAvailableTime대신SourceAvailability를 소비하도록 바꿨습니다. subsequent public contract propagation은020fb1372f390e3f2d719e17244252b7880d85e8및965db4cf9de0250b13272197a2770c851e1e793b까지 반영했습니다.현재 의미 경계:
SystemTime은 TEPP가 source를 관측한 시각,AvailableTime은 그 관측된 evidence가 분석에 사용 가능해진 시각이며 둘은 distinct nominal clocks입니다. receipt wire는 arbitrary timestamp를 self-authenticate하지 않습니다.from_json은 canonical shape/order/encoding 및available_at >= system_observed_at만 검증하고, 신뢰는 owning Evidence repository/service authority가 보장해야 합니다.다음 조건은 #527 exact-head Rust/doc/security gates + independent review 후 protected-main landing입니다. 그 전에는 #524가 이 binding을 authoritative admission input으로 소비하면 안 됩니다.
seonghobae commentedon Sep 15, 2026
추가 self-review에서 wire trust promotion 경로도 닫았습니다. canonical JSON parser가
SourceSnapshotReceiptV1자체를 반환하면, 공격자가 과거 시각을 canonical하게 써서 owner-issued type을 얻을 수 있어 public constructor의 backdating 제거를 우회할 수 있었습니다.RED
21799f6fd0bdebdab1a7d0c68e6148d184bce662→ causal fix4df867279ba530e3171b2f4be8d91c6fe19b0f8c: 이제 arbitrary JSON은ValidatedSourceSnapshotReceiptWireV1으로만 파싱되며, owner-issuedSourceSnapshotReceiptV1로 승격하는 public API는 없습니다.3a0ff875bcf65be95a61c97a680b2e65e5b69db8,8892bb9e88e645c08aa1b5a0204294d9a4655bcd,660e2c91f8acb02ee37280ff22ae64df4ae34bd3에서 export와 public contracts를 전파했습니다.따라서 #529 acceptance는 이제 API/type boundary에서도 반영되어 있습니다. 다만 exact-head hosted gates와 protected-main landing 전이므로 issue는 계속 open으로 유지합니다.
seonghobae commentedon Sep 15, 2026
#529의 clock-owner repair는 #527 안에서 유지되고 있고, 후속 #530가 같은 owner path의 record identity를 더 닫았습니다. RED
7cdaa414e4abd44bb019bc2cbb803fcaadcf714c→ fixdf3ef9ec58ae555092368f4db72c008d8a6e3a15로SourceObservation/SourceAvailability가 각각 Evidence-owned UUIDv7 identity를 가지며 availability가 exactsource_observation_id를 보존합니다. Receipt도 이 두 identity를 bind하므로 owner clock의 출처를 timestamp 조합만으로 추정할 필요가 없습니다.이 변경은 #529의 temporal 의미를 바꾸지 않습니다.
SystemTime과AvailableTime은 계속 별도 nominal clocks이고available_at >= system_observed_at만 강제합니다. arbitrary canonical wire는 계속 validated-but-untrusted type이며 owner-issued state로 승격되지 않습니다. #527 exact-head hosted gates 및 protected-main landing 전까지 #529는 open 유지합니다.