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
3 changes: 3 additions & 0 deletions HARDENING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Acceptance context and exit status

`jep verify --mode acceptance` requires `--expected-audience`. The value is forwarded to the API and negative verification exits with status 2. Malformed input/transport errors exit with status 1. `--ref` accepts either a digest or a JSON reference descriptor. All verification assurance comes from the configured API; the CLI does not perform local cryptographic verification.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,7 @@ jep verify event.json
## License

MIT

## Runtime and verification notes

See [HARDENING.md](HARDENING.md) for supported behavior, regression checks, and compatibility boundaries.
11 changes: 8 additions & 3 deletions jep_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def cmd_create(args: argparse.Namespace) -> int:
if args.aud:
payload["aud"] = args.aud
if args.ref:
payload["ref"] = args.ref
payload["ref"] = parse_json_value(args.ref)
if args.ttl_minutes is not None:
payload["ttl_minutes"] = args.ttl_minutes
if args.digest_only_who:
Expand All @@ -86,9 +86,13 @@ def cmd_verify(args: argparse.Namespace) -> int:
"mode": args.mode,
"consume_nonce": args.consume_nonce,
}
if args.expected_audience:
payload["expected_audience"] = args.expected_audience
if args.mode == "acceptance" and not args.expected_audience:
raise ValueError("acceptance mode requires --expected-audience")
result = build_client(args).verify_event(payload)
print_json(result)
return 0
return 0 if result.get("valid") is True else 2


def cmd_health(args: argparse.Namespace) -> int:
Expand Down Expand Up @@ -130,7 +134,8 @@ def build_parser() -> argparse.ArgumentParser:

verify = sub.add_parser("verify", help="Verify a JEP event")
verify.add_argument("event", help="Event JSON object, event JSON file path, or '-' for stdin")
verify.add_argument("--mode", default="archival", help="Validation mode")
verify.add_argument("--mode", default="archival", choices=["archival", "acceptance"], help="Validation mode")
verify.add_argument("--expected-audience", default="", help="Required receiving audience for acceptance")
verify.add_argument("--consume-nonce", action="store_true", help="Ask verifier to consume nonce")
verify.set_defaults(func=cmd_verify)

Expand Down
16 changes: 16 additions & 0 deletions tests/test_acceptance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import json
from jep_cli import main as module


def test_acceptance_requires_expected_audience(capsys):
assert module.main(["verify", '{"jep":"1"}', "--mode", "acceptance"]) == 1
assert "expected-audience" in capsys.readouterr().err


def test_verification_failure_returns_nonzero(monkeypatch):
class Client:
def verify_event(self, payload):
assert payload["expected_audience"] == "receiver"
return {"valid": False}
monkeypatch.setattr(module, "build_client", lambda args: Client())
assert module.main(["verify", '{}', "--mode", "acceptance", "--expected-audience", "receiver"]) == 2
Loading