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
5 changes: 4 additions & 1 deletion .github/scripts/package_publication_candidate_activation.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
CANDIDATE_NORMAL_DEPENDENCIES = {
CORE_PACKAGE: ["serde", "serde_json", "sha2", "thiserror"],
"ethos-pdf": [CORE_PACKAGE, "serde", "serde_json"],
"ethos-verify": [CORE_PACKAGE, "serde"],
"ethos-verify": [CORE_PACKAGE, "serde", "sha2"],
CONSUMER_PACKAGE: [CORE_PACKAGE, "ethos-pdf", "ethos-verify"],
}
IGNORE_NAMES = {
Expand Down Expand Up @@ -242,6 +242,9 @@ def generated_manifest(package: str) -> str:
[dependencies.serde]
version = "1"
features = ["derive"]

[dependencies.sha2]
version = "0.10"
"""

if package == "ethos-pdf":
Expand Down
5 changes: 5 additions & 0 deletions .github/scripts/test_milestone_d_internal_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,9 +380,14 @@ def discovered_d_contract_schemas() -> list[str]:


def discovered_d_request_envelope_schemas() -> list[str]:
current_d_request_names = {
"ethos-crop-element-request.schema.json",
"ethos-sandbox-subprocess-request.schema.json",
}
return sorted(
str(path.relative_to(ROOT))
for path in (ROOT / "schemas").glob("ethos-*-request.schema.json")
if path.name in current_d_request_names
)


Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- boundary-exception: add source-only `ethos evidence anchor` schema and CLI surface for deterministic evidence refs; no hosted, production, Windows, bundled PDFium, benchmark, parser-quality, table-quality, or release-posture boundary change.
- boundary-exception: refresh patch `0.1.1` execution status for published evaluation surfaces while retaining hosted, production, Windows, bundled PDFium, benchmark, `ethos-doc`, and `ethos-rag` blockers.
- boundary-exception: document bounded patch `0.1.1` public install paths for published evaluation surfaces while retaining hosted, production, Windows, bundled PDFium, benchmark, `ethos-doc`, and `ethos-rag` blockers.
- boundary-exception: close patch `0.1.1` Python PyPI publication with exact registry evidence; no public install wording, hosted, production, Windows, bundled PDFium, benchmark, `ethos-doc`, or `ethos-rag` boundary change.
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,17 @@ The command exits `0` and writes a verification report shaped like this:
}
```

## Evidence anchoring

Ethos can check whether caller-provided evidence refs bind to source document evidence.
This is deterministic source tracing, not semantic validation of an answer.

```bash
./target/debug/ethos evidence anchor schemas/examples/document.example.json \
--evidence-refs schemas/examples/evidence-anchor-request.example.json \
--out /tmp/ethos-evidence-anchor-report.json
```

## Try the alpha verification loop

From a source checkout, the current verification loop is:
Expand Down
67 changes: 67 additions & 0 deletions crates/ethos-cli/src/cmd/evidence.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2026 The Ethos maintainers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

use ethos_core::error::EthosError;
use ethos_core::evidence_anchor::{EvidenceAnchorReport, EvidenceAnchorRequest};
use ethos_grounding_opendataloader_json::OdlJsonSource;

use crate::{
default_max_input_bytes, read_document, read_file_limited, write_output, EvidenceAnchorArgs,
Failure,
};

pub(crate) fn evidence_anchor(args: EvidenceAnchorArgs) -> Result<(), Failure> {
let max_input_bytes = default_max_input_bytes();
let request_bytes = read_file_limited(&args.evidence_refs, max_input_bytes)?;
let request: EvidenceAnchorRequest = serde_json::from_slice(&request_bytes).map_err(|_| {
Failure::Usage("evidence refs file does not match the evidence anchor request shape".into())
})?;

let report = match args.grounding.as_str() {
"ethos-json" => {
let doc = read_document(&args.input)?;
ethos_verify::anchor_evidence(&doc, request)
.map_err(|error| Failure::Usage(error.to_string()))?
}
"opendataloader-json" => {
let bytes = read_file_limited(&args.input, max_input_bytes)?;
let text = String::from_utf8(bytes)
.map_err(|_| Failure::Usage("grounding input is not UTF-8".to_string()))?;
let source = OdlJsonSource::from_json_str(&text)
.map_err(|e| Failure::Usage(format!("opendataloader-json adapter: {e}")))?;
ethos_verify::anchor_evidence(&source, request)
.map_err(|error| Failure::Usage(error.to_string()))?
}
other => {
return Err(Failure::Usage(format!(
"unknown grounding adapter '{other}' (available: ethos-json, opendataloader-json)"
)));
}
};

write_anchor_report(args.out, &report)
}

fn write_anchor_report(
out: Option<std::path::PathBuf>,
report: &EvidenceAnchorReport,
) -> Result<(), Failure> {
let value = serde_json::to_value(report).map_err(|e| EthosError::internal(e.to_string()))?;
let mut bytes =
ethos_core::c14n::c14n_bytes(&value).map_err(|e| EthosError::internal(e.message))?;
bytes.push(b'\n');
write_output(out, &bytes)
}
1 change: 1 addition & 0 deletions crates/ethos-cli/src/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub(crate) mod crop;
pub(crate) mod crop_artifacts;
pub(crate) mod doc;
pub(crate) mod doctor;
pub(crate) mod evidence;
pub(crate) mod rag;
pub(crate) mod security;
pub(crate) mod verify;
29 changes: 29 additions & 0 deletions crates/ethos-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ enum Command {
#[command(subcommand)]
command: SecurityCommand,
},
/// Deterministic evidence anchoring
Evidence {
#[command(subcommand)]
command: EvidenceCommand,
},
/// Citation evidence verification (ethos-verify)
Verify(VerifyArgs),
/// Recompute and check a document fingerprint
Expand Down Expand Up @@ -214,6 +219,12 @@ enum SecurityCommand {
Report(SecurityReportArgs),
}

#[derive(Subcommand)]
enum EvidenceCommand {
/// Check caller-provided evidence refs against source evidence
Anchor(EvidenceAnchorArgs),
}

#[derive(Args)]
pub(crate) struct RagChunkArgs {
/// Canonical document (`*.ethos.json`)
Expand All @@ -232,6 +243,21 @@ pub(crate) struct SecurityReportArgs {
pub(crate) out: Option<PathBuf>,
}

#[derive(Args)]
pub(crate) struct EvidenceAnchorArgs {
/// Grounding input: canonical Ethos document, or foreign output with --grounding
pub(crate) input: PathBuf,
/// Evidence refs request JSON.
#[arg(long)]
pub(crate) evidence_refs: PathBuf,
/// Grounding adapter id: ethos-json or opendataloader-json.
#[arg(long, default_value = "ethos-json")]
pub(crate) grounding: String,
/// Output path for evidence_anchor_report.json (default: stdout)
#[arg(long)]
pub(crate) out: Option<PathBuf>,
}

#[derive(Args)]
pub(crate) struct VerifyArgs {
/// Grounding input: canonical Ethos document, or foreign output with --grounding
Expand Down Expand Up @@ -363,6 +389,9 @@ fn run(cli: Cli) -> Result<(), Failure> {
Command::Security {
command: SecurityCommand::Report(args),
} => cmd::security::security_report(args),
Command::Evidence {
command: EvidenceCommand::Anchor(args),
} => cmd::evidence::evidence_anchor(args),
Command::Verify(args) => cmd::verify::verify(args),
Command::Fingerprint(args) => cmd::doc::fingerprint(args),
Command::Doctor(args) => cmd::doctor::doctor(args),
Expand Down
Loading
Loading