Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions crates/echo-cas/tests/keep_identity_bridge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: Apache-2.0
// © James Ross Ω FLYING•ROBOTS <https://github.com/flyingrobots>
//! Same-source Echo and pinned Keep identity-vector witness.
//!
//! The raw Echo vectors were generated independently with `b3sum` 1.8.5. The
//! Keep vectors and preimage are pinned to Keep commit
//! `3bf7b9179db41e90620e6d1875c2d40222a2330b`.

use std::num::TryFromIntError;

use echo_cas::blob_hash;

const KEEP_BLOB_DATA_MAGIC: &[u8; 16] = b"KEEP:BLOB:DATA\0\0";
const KEEP_BLOB_IDENTITY_VERSION: [u8; 2] = 1_u16.to_be_bytes();
const KEEP_BLOB_HASH_ALGORITHM: [u8; 1] = [1];

#[test]
fn same_exact_sources_reproduce_echo_and_pinned_keep_identity_vectors(
) -> Result<(), TryFromIntError> {
assert_identity_pair(
&[],
"af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262",
"c0074a279c09f9d019dc10e4c821f79f1450cfb8541ab4627132ab9f3c75e33f",
)?;
assert_identity_pair(
b"Keep exact bytes.\n",
"3d4f69a42f4cde6629970fe61d548f60d475a0f9ac26a24be5fab204b6eb030a",
"af75d70e4993121254ac71f16c5edd02410a36f94d795e4d6064ed3122b7967d",
)?;

let byte_ramp = (u8::MIN..=u8::MAX).collect::<Vec<_>>();
assert_identity_pair(
&byte_ramp,
"4a495ba42461748eca8fdad618f976aa726cc2903de9fcb40735a786ac1c196b",
"e782f90f48483f6a8520c9b05eca57ace1647374dd9456b9e41aadccacd10f12",
)?;
Ok(())
}

fn assert_identity_pair(
bytes: &[u8],
expected_echo: &str,
expected_keep: &str,
) -> Result<(), TryFromIntError> {
let echo = blob_hash(bytes);
let keep = keep_blob_digest_v1(bytes)?;
assert_eq!(
blake3::Hash::from_bytes(*echo.as_bytes()).to_hex().as_str(),
expected_echo
);
assert_eq!(keep.to_hex().as_str(), expected_keep);
assert_ne!(echo.as_bytes(), keep.as_bytes());
Ok(())
}

fn keep_blob_digest_v1(bytes: &[u8]) -> Result<blake3::Hash, TryFromIntError> {
let logical_length = u64::try_from(bytes.len())?;
let mut hasher = blake3::Hasher::new();
hasher.update(KEEP_BLOB_DATA_MAGIC);
hasher.update(&KEEP_BLOB_IDENTITY_VERSION);
hasher.update(&KEEP_BLOB_HASH_ALGORITHM);
hasher.update(bytes);
hasher.update(&logical_length.to_be_bytes());
Ok(hasher.finalize())
}
42 changes: 37 additions & 5 deletions docs/architecture/echo-keep-physical-content-boundary.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
implemented on this branch.
- **Refines:** [Retained reading storage and proof boundary](../adr/0020-retained-reading-storage-and-proof-boundary.md)
- **Depends on:** [Durable external-action settlement](../adr/0026-durable-external-action-settlement.md)
- **Related:** [Keep authenticated reconstruction contract](https://github.com/flyingrobots/keep/blob/3bf7b9179db41e90620e6d1875c2d40222a2330b/docs/architecture/authenticated-reconstruction-contract.md)
- **Related:** [Keep authenticated reconstruction contract](https://github.com/flyingrobots/keep/blob/3bf7b9179db41e90620e6d1875c2d40222a2330b/docs/invariants/authenticated-reconstruction/README.md)

## Decision

Expand Down Expand Up @@ -205,11 +205,36 @@ Echo may admit an evidenced refusal as an observation only under an Echo law
that explicitly accepts that refusal class and its physical aperture. A Rust
error alone is not a witnessed refusal.

## Identity bridge
## Identity bridge decision

Echo `BlobHash` and Keep `BlobId` are distinct typed identities. They must not
be cast, substituted, or equated because both currently use 32-byte BLAKE3
digests.
Echo `BlobHash` and Keep `BlobId` are distinct typed identities. The
experimental bridge retains both; it defines no digest cast or implicit
conversion.

| Question | Accepted experimental decision |
| ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| What is Echo's current content identity? | [`BlobHash`](../../crates/echo-cas/src/lib.rs) contains the 32-byte output of raw `BLAKE3(P)` for exact content bytes `P`. Its version and algorithm are implicit in the current API. |
| Does Echo identity include logical length? | No. [`RetainedBlobDescriptor`](../../crates/echo-cas/src/retention.rs) and CAS-addressed [WSC references](../../crates/warp-core/src/wsc/store.rs) carry byte length beside the hash, not inside `BlobHash`. |
| What is Keep's current logical identity? | Keep `BlobId` v1 contains identity version 1, algorithm 1, logical length, and the differently framed digest specified below under the [pinned Keep identity contract](https://github.com/flyingrobots/keep/blob/3bf7b9179db41e90620e6d1875c2d40222a2330b/docs/adr/0001-exact-logical-byte-identity.md). |
| Can Echo identity become Keep identity without bytes? | No. Even an Echo hash plus an asserted length cannot calculate Keep's differently framed digest. The adapter needs the exact bytes or a previously admitted binding witnessed from those bytes. |
| Is the conversion one-to-one? | No structural conversion is admitted. One exact stream deterministically produces one Echo-and-Keep pair under the stated hash laws, subject to the collision-resistance assumption, but neither digest output proves a unique preimage. |
| Which identity remains in Echo WAL and WSC? | Echo's existing raw content hash remains unchanged. Keep identity accompanies it only in the adapter's physical-evidence and binding posture. |
| What happens when a version or algorithm differs? | The adapter reports a typed identity-contract incompatibility before publication. It never reinterprets or truncates either coordinate. |
| Are both identities retained during experiment and migration? | Yes, together with exact logical length and the identity-contract versions used to establish their relation. |

The two exact preimages are therefore:

```text
Echo: BLAKE3(P)

Keep: BLAKE3(
"KEEP:BLOB:DATA\0\0"
|| 0x0001
|| 0x01
|| P
|| len(P) as u64 big-endian
)
```

The adapter establishes their relation by applying both identity laws to one
exact source stream and then verifying reconstruction:
Expand All @@ -228,6 +253,13 @@ Keep's `stage_expected` can verify an expected Keep `BlobId`; it cannot by
itself prove an Echo-to-Keep identity relation. The adapter owns the second
identity calculation and the binding witness.

The initial [same-source identity witness](../../crates/echo-cas/tests/keep_identity_bridge.rs)
checks externally generated Echo hashes and the pinned Keep v1 golden vectors
for the same bytes. It does not execute Keep, reconstruct retained content, or
prove the route-independent half of the bridge. Issue #722 retains that gate:
reconstruction through the experimental Keep adapter must reproduce the Echo
hash and exact length before the bridge is conforming.

## Semantic observation and physical evidence

Ordinary Echo semantics and Keep realization provenance remain separate:
Expand Down
Loading