Skip to content
Closed
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
109 changes: 36 additions & 73 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,120 +1,83 @@
# JEP CLI v0.6
# JEP CLI — JEP Core 0.7

Command-line tool for creating and verifying JEP v0.6 events through the JEP API.
Command-line client for the current JEP Core 0.7 reference API.

This CLI targets:
Default commands use:

```text
POST /events/create
POST /events/verify
POST /v0.7/events/create
POST /v0.7/events/verify
GET /health
```

It is aligned with:
Historical pre-0.7 verification is explicit through `jep verify-legacy`.
The CLI never retries a failed 0.7 event as 0.6.

- `draft-wang-jep-judgment-event-protocol-06`
- `draft-wang-jep-profiles-00`
- `draft-wang-jep-conformance-00`
- `hjs-spec/jep-api`

## Status

Experimental implementation seed.

This CLI does not define new JEP-Core semantics and does not determine legal liability, factual truth, regulatory compliance, or complete-log availability.

## Installation
## Install

```bash
pip install -e ".[dev]"
```

## Configuration

Use flags:

```bash
jep --base-url http://127.0.0.1:8000 health
```

Or environment variables:

```bash
export JEP_API_URL=http://127.0.0.1:8000
export JEP_API_KEY=
```

## Commands

### Health

```bash
jep health
```

### Create a Judgment event
## Create a J event

```bash
jep create \
--verb J \
--who did:example:agent-789 \
--what '{"claim":"approve","subject":"demo"}' \
--aud https://api.example.org
--what '{"claim":"approve","subject":"demo"}'
```

### Create with extensions
For D/T/V, provide the Core 0.7 minimum structures:
- D: `what.delegatee` + `what.scope`
- T: `--ref` + `what.termination_scope`
- V: `--ref` + `what.verification_scope` + `what.result`

```bash
jep create \
--verb J \
--who did:example:agent-789 \
--what '{"claim":"approve"}' \
--ext 'https://example.org/profile={"name":"demo"}' \
--ext-crit https://example.org/profile
```

### Verify an event from a file
## Verify

```bash
jep verify event.json --mode archival
```

### Pipe create response into verify
Current validation output uses:
- `status = valid | invalid | indeterminate`
- independent `checks`
- `event_identity`
- `event_hash`
- optional `acceptance`

## Explicit legacy verification

```bash
jep create --verb J --who did:example:agent --what '{"claim":"approve"}' > response.json
jep extract-event response.json > event.json
jep verify event.json
jep verify-legacy historical-event.json --mode archival
```

## Input Rules
Use this only when the artifact is already known to be pre-0.7.

`--what` and event arguments may be:
## Boundaries

- inline JSON;
- a plain string;
- a file path;
- `-` for stdin.
JEP Core validity does not establish substantive truth, legal effect,
authorization validity, causality, policy outcome, or complete-log status.

## Related Repositories
## Related repositories

- JEP v0.6: https://github.com/hjs-spec/jep-v06
- JEP API v0.6: https://github.com/hjs-spec/jep-api
- JEP Python SDK v0.6: https://github.com/hjs-spec/jep-sdk-py
- JEP Go SDK v0.6: https://github.com/hjs-spec/jep-sdk-go
- HJS v0.5: https://github.com/hjs-spec/hjs-05
- JAC v0.5: https://github.com/hjs-spec/jac-agent-02
- JEP Core: https://github.com/hjs-spec/jep-core
- JEP API: https://github.com/hjs-spec/jep-api
- Python SDK: https://github.com/hjs-spec/sdk-py
- JavaScript SDK: https://github.com/hjs-spec/sdk-js
- Go SDK: https://github.com/hjs-spec/sdk-go

## Public Drafts
## Public draft

- JEP-Core: https://datatracker.ietf.org/doc/draft-wang-jep-judgment-event-protocol/
- JEP-Profiles: https://datatracker.ietf.org/doc/draft-wang-jep-profiles/
- JEP-Conformance: https://datatracker.ietf.org/doc/draft-wang-jep-conformance/
https://datatracker.ietf.org/doc/draft-wang-jep-judgment-event-protocol/

## License

MIT

## Runtime and verification notes

See [HARDENING.md](HARDENING.md) for supported behavior, regression checks, and compatibility boundaries.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.6.1
0.7.0
16 changes: 11 additions & 5 deletions jep_cli/client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
"""Small HTTP client for JEP CLI v0.6."""
"""HTTP client for JEP Core 0.7 CLI.

Current endpoints are versioned. Historical pre-0.7 verification is explicit.
"""

from __future__ import annotations

Expand Down Expand Up @@ -26,10 +29,13 @@ def __init__(self, base_url: str = "http://127.0.0.1:8000", api_key: str = "", t
self.timeout = timeout

def create_event(self, payload: Dict[str, Any]) -> Dict[str, Any]:
return self._json("POST", "/events/create", payload)
return self._json("POST", "/v0.7/events/create", payload)

def verify_event(self, payload: Dict[str, Any]) -> Dict[str, Any]:
return self._json("POST", "/events/verify", payload)
return self._json("POST", "/v0.7/events/verify", payload)

def verify_event_legacy(self, payload: Dict[str, Any]) -> Dict[str, Any]:
return self._json("POST", "/events/verify-legacy", payload)

def health(self) -> Dict[str, Any]:
return self._json("GET", "/health", None)
Expand All @@ -38,7 +44,7 @@ def _json(self, method: str, path: str, payload: Optional[Dict[str, Any]]) -> Di
data = None
headers = {
"content-type": "application/json",
"user-agent": "JEP-CLI/0.6.0",
"user-agent": "JEP-CLI/0.7.0",
}
if self.api_key:
headers["authorization"] = f"Bearer {self.api_key}"
Expand All @@ -57,5 +63,5 @@ def _json(self, method: str, path: str, payload: Optional[Dict[str, Any]]) -> Di
parsed = json.loads(body)
except Exception:
parsed = {"message": body}
msg = parsed.get("message") or parsed.get("error") or body
msg = parsed.get("detail") or parsed.get("message") or parsed.get("error") or body
raise JEPAPIError(exc.code, msg, parsed) from exc
29 changes: 21 additions & 8 deletions jep_cli/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""JEP CLI v0.6 command line interface."""
"""JEP Core 0.7 command line interface."""

from __future__ import annotations

Expand Down Expand Up @@ -84,14 +84,22 @@ def cmd_verify(args: argparse.Namespace) -> int:
payload = {
"event": event,
"mode": args.mode,
"consume_nonce": args.consume_nonce,
}
if args.max_age_seconds is not None:
payload["max_age_seconds"] = args.max_age_seconds
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 if result.get("status") == "valid" else 2


def cmd_verify_legacy(args: argparse.Namespace) -> int:
event = parse_json_value(args.event)
if not isinstance(event, dict):
raise ValueError("event must be JSON object or path to JSON event file")
result = build_client(args).verify_event_legacy({"event": event, "mode": args.mode})
print_json(result)
return 0 if result.get("valid") is True else 2


Expand All @@ -112,7 +120,7 @@ def cmd_extract_event(args: argparse.Namespace) -> int:
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="jep",
description="JEP CLI v0.6 for creating and verifying JEP-Core events through the JEP API.",
description="JEP CLI for JEP Core 0.7. Legacy pre-0.7 verification is explicit.",
)
parser.add_argument("--base-url", default="", help="JEP API base URL. Default: JEP_API_URL or http://127.0.0.1:8000")
parser.add_argument("--api-key", default="", help="API key. Default: JEP_API_KEY")
Expand All @@ -125,7 +133,7 @@ def build_parser() -> argparse.ArgumentParser:
create.add_argument("--who", default="", help="Actor identifier")
create.add_argument("--what", required=True, help="JSON value, string, file path, or '-' for stdin")
create.add_argument("--aud", default="", help="Audience / validation context")
create.add_argument("--ref", default="", help="Reference event hash")
create.add_argument("--ref", default="", help="Typed reference JSON, digest, or file path")
create.add_argument("--ttl-minutes", type=int, default=None, help="Optional TTL extension in minutes")
create.add_argument("--digest-only-who", action="store_true", help="Request digest-only privacy extension")
create.add_argument("--ext", action="append", default=[], help="Extension key=json_or_string. May be repeated")
Expand All @@ -135,10 +143,15 @@ 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", 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.add_argument("--expected-audience", default="", help="Expected audience when the selected profile requires it")
verify.add_argument("--max-age-seconds", type=int, default=None, help="Optional profile freshness window")
verify.set_defaults(func=cmd_verify)

legacy = sub.add_parser("verify-legacy", help="Explicitly verify a historical pre-0.7 event")
legacy.add_argument("event", help="Historical event JSON object, file path, or '-' for stdin")
legacy.add_argument("--mode", default="archival", choices=["archival", "acceptance"])
legacy.set_defaults(func=cmd_verify_legacy)

health = sub.add_parser("health", help="Check JEP API health")
health.set_defaults(func=cmd_health)

Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"

[project]
name = "jep-cli"
version = "0.6.1"
description = "Command-line tool for creating and verifying JEP v0.6 events through the JEP API"
version = "0.7.0"
description = "Command-line tool for creating and verifying JEP Core 0.7 events through the JEP API"
readme = "README.md"
license = { text = "MIT" }
authors = [
Expand Down
32 changes: 20 additions & 12 deletions tests/test_acceptance.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
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 test_acceptance_does_not_require_core_audience(monkeypatch, capsys):
class FakeClient:
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
assert payload["mode"] == "acceptance"
assert "expected_audience" not in payload
return {
"status": "valid",
"mode": "acceptance",
"profile": "jep-core-0.7",
"checks": {"syntax": "pass", "cryptographic": "pass", "event_identity": "pass"},
"acceptance": {"outcome": "accepted", "effect_applied": True},
}

monkeypatch.setattr(module, "build_client", lambda args: FakeClient())
code = module.main([
"verify",
'{"jep":"1","id":"urn:uuid:x","verb":"J","who":"a","when":1,"what":{"claim":"x"},"sig":"s"}',
"--mode", "acceptance",
])
assert code == 0
assert '"status": "valid"' in capsys.readouterr().out
Loading
Loading