Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
The generic `run-edict-operation --serve` driver supports repeated operations,
historical outcome lookup, and derived observation-change discovery.

- An opt-in create-operation configuration v2 derives Unicode scalar key bounds in both provider components and binds them into packages. Runtime admission enforces nonempty keys and separate scalar/UTF-8 byte ceilings; legacy configuration v1 is unchanged.

- A disabled-by-default experimental Keep ReferenceStore adapter implements the complete-object CAS port with explicit limits, private bindings, sanitized backend errors and atomic output promotion. It preserves existing CAS defaults and claims no restart durability.

- An isolated experimental Echo–Keep identity bridge checks both independent hash laws and exact byte length from one bounded stream. It leaves the default CAS dependency graph unchanged.
Expand Down
4 changes: 2 additions & 2 deletions crates/echo-edict-provider-lowerer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ proposal constructor supports mutations and refuses a `Query`; authored reads
remain a separate bounded observer/optic path and must never be represented as
synthetic mutations.

The refreshed 461,184-byte checked lowerer component has SHA-256
`80e426cc277a4c5e78eab374b68144c8f3e34b6afb93eb991789ce2e5ee7b9a4` and has
The refreshed 465,369-byte checked lowerer component has SHA-256
`513b1efcf9d4c340180fbb66d01633bb05711f58620860f78e26412c54b538fc` and has
crossed the reproducible promotion boundary. The pinned Edict host admits its
generated envelope under the owning `generated-artifact` CDDL root, and the
isolated host fixture exercises the exact helper binding, typed codecs, EINT
Expand Down
120 changes: 115 additions & 5 deletions crates/echo-edict-provider-lowerer/src/executable_operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ struct ProgramConfiguration<'a> {
write_bytes: u64,
node_key_field: &'a str,
replacement_field: &'a str,
key_max_scalars: Option<u64>,
}

struct ApplicationIntent<'a> {
Expand Down Expand Up @@ -251,7 +252,7 @@ pub(super) fn lower(request: &LoweringRequestV1) -> Result<LoweringSuccessV1, Pr
closure.lawpack,
&intent,
)?;
let configuration = validate_configuration(&configuration)?;
let configuration = validate_configuration(&configuration, &core, &intent)?;
if configuration.program != intent.program {
return Err(super::unsupported_semantics(
"target-configuration.operation-profile",
Expand Down Expand Up @@ -1225,10 +1226,14 @@ fn validate_target_ir(
Ok(())
}

fn validate_configuration(
value: &CanonicalValueV1,
) -> Result<ProgramConfiguration<'_>, ProviderRefusalV1> {
if text_field(value, "apiVersion") != Some(CONFIGURATION_ABI) {
fn validate_configuration<'a>(
value: &'a CanonicalValueV1,
core: &CanonicalValueV1,
intent: &ApplicationIntent<'_>,
) -> Result<ProgramConfiguration<'a>, ProviderRefusalV1> {
let bounded_keys =
text_field(value, "apiVersion") == Some("echo.operation-lowering-configuration/v2");
if !bounded_keys && text_field(value, "apiVersion") != Some(CONFIGURATION_ABI) {
return Err(super::unsupported_semantics(
"target-configuration.echo-operation",
));
Expand All @@ -1242,6 +1247,12 @@ fn validate_configuration(
))
}
};
// The v2 selector derives a key bound for the create operation only.
if bounded_keys && !matches!(program, OperationProgram::Create) {
return Err(super::unsupported_semantics(
"target-configuration.echo-operation",
));
}
let invocation = required_map(
value,
"invocationBinding",
Expand Down Expand Up @@ -1324,6 +1335,11 @@ fn validate_configuration(
write_bytes: required_u64(budget, "writeBytes", "target-configuration.echo-operation")?,
node_key_field,
replacement_field,
key_max_scalars: if bounded_keys {
Some(derive_key_scalar_limit(core, intent.name, node_key_field)?)
} else {
None
},
};
if configuration.max_replacement_bytes == 0
|| configuration.steps
Expand All @@ -1342,6 +1358,61 @@ fn validate_configuration(
Ok(configuration)
}

fn derive_key_scalar_limit(
core: &CanonicalValueV1,
intent_name: &str,
field: &str,
) -> Result<u64, ProviderRefusalV1> {
let subject = "core.echo-operation.key-bound";
let coordinate = required_text(core, "coordinate", subject)?;
let prefix = format!("{coordinate}.");
let types = required_map(core, "types", subject)?;
let intents = required_map(core, "intents", subject)?;
let intent = required_map(intents, intent_name, subject)?;
let input = required_text(intent, "input", subject)?;
let input_name = input.strip_prefix(&prefix).unwrap_or(input);
let input = required_map(types, input_name, subject)?;
if text_field(input, "kind") != Some("Record") {
return Err(super::unsupported_semantics(subject));
}
let fields = required_map(input, "fields", subject)?;
let key_type = required_text(fields, field, subject)?;
let max = if let Some(spec) = key_type
.strip_prefix("String<max=")
.and_then(|s| s.strip_suffix('>'))
{
let (max, canonical) = spec
.split_once(",canonical=")
.ok_or_else(|| super::unsupported_semantics(subject))?;
if !matches!(canonical, "raw-utf8" | "unicode-scalar-nfc") {
return Err(super::unsupported_semantics(subject));
}
let parsed = max
.parse::<u64>()
.map_err(|_| super::unsupported_semantics(subject))?;
if parsed.to_string() != max {
return Err(super::unsupported_semantics(subject));
}
parsed
} else {
let key_name = key_type.strip_prefix(&prefix).unwrap_or(key_type);
let key = required_map(types, key_name, subject)?;
if text_field(key, "kind") != Some("String")
|| !matches!(
text_field(key, "canonical"),
Some("raw-utf8" | "unicode-scalar-nfc")
)
{
return Err(super::unsupported_semantics(subject));
}
required_u64(key, "max", subject)?
};
if max == 0 || max > 65_536 {
return Err(super::unsupported_semantics(subject));
}
Ok(max)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

fn validate_result_projection(
value: &CanonicalValueV1,
input: &SemanticInput,
Expand Down Expand Up @@ -1691,6 +1762,27 @@ fn encode_package(
CanonicalValueV1::Array(vec![canonical_text(field)]),
));
}
if let (Some(max), CanonicalValueV1::Map(fields)) =
(configuration.key_max_scalars, &mut projection_value)
{
fields.push((
canonical_text("application_input_key_bound"),
canonical_map([
(
"schema",
canonical_text("echo.application-input-key-bound/v1"),
),
(
"max_unicode_scalars",
CanonicalValueV1::Integer(i128::from(max)),
),
(
"max_utf8_bytes",
CanonicalValueV1::Integer(i128::from((max * 4).min(65_536))),
),
]),
));
}
let package = canonical_map([
("application_result_projection", projection_value),
(
Expand Down Expand Up @@ -1920,3 +2012,21 @@ fn profile_digest(label: &str) -> [u8; 32] {
fn hash_value(hash: [u8; 32]) -> CanonicalValueV1 {
CanonicalValueV1::Bytes(hash.to_vec())
}

#[cfg(test)]
mod key_bound_tests {
use super::*;

#[test]
fn compiler_produced_imported_key_type_resolves_scalar_bound() -> Result<(), String> {
let core = echo_edict_canonical::decode_canonical_cbor_v1(include_bytes!(
"../../echo-edict-provider-verifier/tests/fixtures/compiler-produced-key-bound/create-greeting.core.cbor"
)).map_err(|error| format!("{error:?}"))?;
assert_eq!(
derive_key_scalar_limit(&core, "createGreeting", "key")
.map_err(|error| format!("{error:?}"))?,
64
);
Ok(())
}
}
4 changes: 2 additions & 2 deletions crates/echo-edict-provider-lowerer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,7 @@ pub mod echo_dpo {
pub const PROVIDER_SCHEMA_COORDINATE: &str = "echo.provider-artifacts.cddl@1";
/// Raw SHA-256 of the exact self-contained provider CDDL bytes.
pub const PROVIDER_SCHEMA_SHA256_HEX: &str =
"16e8b1aa45f77bf2f69f538e7120cfb398d85525fdc6947a8cdd86d7b317ea2e";
"139ac50b29f4a6b524febd37e1735e9de3c905fe4b234779b705d68a19cd29e7";
/// Exact generated-artifact profile coordinate owning operation schemas.
pub const GENERATED_ARTIFACT_PROFILE: &str = "echo.dpo.registration/v1";
/// Digest-framing domain for the generated-artifact profile.
Expand Down Expand Up @@ -846,7 +846,7 @@ pub mod echo_dpo {

const MUTATION_RULE_NAME: &str = concat!(
"cmd/contract/",
"16e8b1aa45f77bf2f69f538e7120cfb398d85525fdc6947a8cdd86d7b317ea2e",
"139ac50b29f4a6b524febd37e1735e9de3c905fe4b234779b705d68a19cd29e7",
"/3389142194/a.b@1.t"
);
const PROVIDER_OPERATIONS: [ProviderOperationV1<'static>; 1] = [ProviderOperationV1 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub mod echo_dpo {
pub const PROVIDER_SCHEMA_COORDINATE: &str = "echo.provider-artifacts.cddl@1";
/// Raw SHA-256 of the exact self-contained provider CDDL bytes.
pub const PROVIDER_SCHEMA_SHA256_HEX: &str =
"16e8b1aa45f77bf2f69f538e7120cfb398d85525fdc6947a8cdd86d7b317ea2e";
"139ac50b29f4a6b524febd37e1735e9de3c905fe4b234779b705d68a19cd29e7";
/// Exact generated-artifact profile coordinate owning operation schemas.
pub const GENERATED_ARTIFACT_PROFILE: &str = "echo.dpo.registration/v1";
/// Digest-framing domain for the generated-artifact profile.
Expand Down Expand Up @@ -108,7 +108,7 @@ pub mod echo_dpo {

const MUTATION_RULE_NAME: &str = concat!(
"cmd/contract/",
"16e8b1aa45f77bf2f69f538e7120cfb398d85525fdc6947a8cdd86d7b317ea2e",
"139ac50b29f4a6b524febd37e1735e9de3c905fe4b234779b705d68a19cd29e7",
"/3389142194/a.b@1.t"
);
const PROVIDER_OPERATIONS: [ProviderOperationV1<'static>; 1] = [ProviderOperationV1 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const REVIEW_MEDIA_TYPE: &str = "application/json";
const GENERATED_SOURCE_PATH: &str = "generated/echo_dpo.rs";
const REVIEW_PATH: &str = "review/echo_dpo.json";
const EXPECTED_PROVIDER_SCHEMA_SHA256_HEX: &str =
"16e8b1aa45f77bf2f69f538e7120cfb398d85525fdc6947a8cdd86d7b317ea2e";
"139ac50b29f4a6b524febd37e1735e9de3c905fe4b234779b705d68a19cd29e7";
const EXPECTED_OPERATION_ID_LAW: &str = "echo.semantic-operation-id.fnv1-32/v1";
const EXPECTED_OPERATION_ID: u32 = 3_389_142_194;

Expand Down
4 changes: 2 additions & 2 deletions crates/echo-edict-provider-verifier/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ route's fixed 64 KiB output ceiling.

The `wasm32` guest adapter vendors Edict's exact frozen
`edict:target-provider/verifier@1.0.0` WIT world and performs only exhaustive
transport-to-model conversion. Its reproducibly built 473,642-byte checked
transport-to-model conversion. Its reproducibly built 477,731-byte checked
component has SHA-256
`87649ace3aa1ab21c45cc4ed5c90051583dd08b48518cdca61dacde72783440a`.
`7206b0b9c97d89adf769552e7a400c1ba8a049d02756a5a94534328fbde21696`.
Component identity and admitted host replay remain separate propositions: the
pinned Edict host preflights the request artifacts and declared output schema,
invokes the checked component, then admits and manifests each returned accepted
Expand Down
Loading
Loading