Skip to content
This repository was archived by the owner on Sep 26, 2026. It is now read-only.
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
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jep-runtime --help

Version 0.2 uses `jep-runtime`; `jep` remains owned by `jep-cli`. Python imports and existing local-runtime archives are unchanged. If version 0.1 shared an environment with `jep-cli`, upgrade this package first, then run `python -m pip install --force-reinstall jep-cli` to restore the previously shared executable.

This runtime uses its own event envelope. For signed JEP-Core-0.6 wire events use [the SDK](https://github.com/hjs-spec/sdk-py) and [API](https://github.com/hjs-spec/jep-api); do not submit local-runtime envelope fields as core top-level fields.
This runtime retains a historical internal archive/replay envelope. It is **not** the JEP Core 0.7 wire object. For signed Core 0.7 wire events use [the SDK](https://github.com/hjs-spec/sdk-py) and [API](https://github.com/hjs-spec/jep-api). The module `jep_runtime.core.wire07` exposes the Core 0.7 minimum structural boundary. Runtime fields such as `nonce`, `previous_event_hash`, `delegation_chain`, `authority_scope`, and `verification_state` are runtime/profile state and are not required Core fields.

## Architecture

Expand Down Expand Up @@ -44,7 +44,7 @@ This runtime uses its own event envelope. For signed JEP-Core-0.6 wire events us
4. New events reference `previous_event_hash`, producing an append-only event chain.
5. Delegation events carry bounded `authority_scope` and `delegation_chain` entries so authority lineage can be replayed.
6. JSONL archives append one canonical event record per line.
7. Verification recomputes hashes, validates nonce uniqueness, checks hash continuity, validates delegation scope, and invokes the configured neutral profile adapter.
7. Runtime verification recomputes internal-envelope hashes, may validate profile/runtime nonce uniqueness, checks internal hash continuity, validates delegation scope, and invokes the configured profile adapter. These are runtime/chain behaviors, not JEP Core validation requirements.

## Replay flow

Expand Down Expand Up @@ -89,7 +89,7 @@ jep-runtime conformance-test
| Canonicalization | Stable UTF-8 JSON with sorted keys and normalized strings |
| Deterministic hashing | SHA-256 over canonical event without `event_hash` |
| Delegation semantics | Parent/child scope and expiration checks |
| Verification semantics | Hash, nonce, timestamp, profile, and chain integrity checks |
| Runtime verification | Internal envelope hash, optional/profile nonce state, profile checks, and runtime chain integrity |
| Profile compatibility | Neutral `ProfileAdapter` contract with mock OAuth/OIDC, X509, DID/VC, Local IAM labels |
| Replay correctness | Archive replay must re-verify the full chain and emit lineage graph/state |

Expand All @@ -103,7 +103,7 @@ jep-runtime conformance-test
| `D` Delegation | `delegate_authority()`, scoped delegation events, `verify_delegation_chain()` |
| `T` Termination | `EventType.TERMINATION`, replayed into `termination_state` |
| `V` Verification | `verify_event()`, `verify_chain()`, `verify_replay()`, verification events |
| Replay protection | Required `nonce` and duplicate nonce validation |
| Core safe retry | Stable Event Identity and idempotent acceptance belong to JEP Core 0.7; the internal runtime envelope may additionally use nonce/profile state |
| Signed/verifiable event format | Immutable hashed event model plus mock profile references |
| Optional profiles | `ProfileAdapter` interface; provider-neutral mock adapter |
| Append-only receipts | JSONL archive with chain verification on replay |
Expand Down
8 changes: 5 additions & 3 deletions jep_runtime/core/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,11 @@ def _thaw(value: Any) -> Any:
class JEPEvent:
"""Immutable JEP event after hash assignment.

The runtime keeps J/D/T/V semantics minimal: events are typed evidence
records, linked by previous_event_hash, scoped by authority_scope, and
replayable through nonce, timestamp, profile, and delegation_chain.
This class is the runtime's historical internal archive/replay envelope,
not the JEP Core 0.7 wire object. Runtime fields such as nonce,
previous_event_hash, delegation_chain, authority_scope, and
verification_state are profile/runtime state and MUST NOT be interpreted
as required JEP Core fields. See core.wire07 for the Core 0.7 boundary.
"""

event_id: str
Expand Down
79 changes: 79 additions & 0 deletions jep_runtime/core/wire07.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""JEP Core 0.7 wire-shape helpers.

This module is deliberately separate from the runtime's historical internal
archive envelope. JEP Core 0.7 does not require runtime fields such as nonce,
previous_event_hash, delegation_chain, authority_scope, or verification_state.
"""

from __future__ import annotations

from typing import Any, Mapping

CORE07_REQUIRED = ("jep", "id", "verb", "who", "when", "what")
CORE07_VERBS = frozenset({"J", "D", "T", "V"})


class Core07ShapeError(ValueError):
"""Raised when a mapping does not satisfy the JEP Core 0.7 minimum shape."""


def event_identity(event: Mapping[str, Any]) -> tuple[str, str]:
"""Return stable JEP Event Identity (who,id) after minimum validation."""
validate_core07_shape(event)
return str(event["who"]), str(event["id"])


def unsigned_event(event: Mapping[str, Any]) -> dict[str, Any]:
"""Return the signed payload object with sig omitted."""
validate_core07_shape(event, require_signature=False)
return {str(k): v for k, v in event.items() if k != "sig"}


def validate_core07_shape(
event: Mapping[str, Any], *, require_signature: bool = True
) -> None:
"""Validate the Core 0.7 structural minimum.

This is not cryptographic, trust-profile, chain, or policy validation.
"""
if not isinstance(event, Mapping):
raise Core07ShapeError("event must be an object")

missing = [name for name in CORE07_REQUIRED if name not in event]
if require_signature and "sig" not in event:
missing.append("sig")
if missing:
raise Core07ShapeError("missing required field(s): " + ", ".join(missing))

if event["jep"] != "1":
raise Core07ShapeError('jep must be "1"')
if event["verb"] not in CORE07_VERBS:
raise Core07ShapeError("verb must be J, D, T, or V")
if not isinstance(event["id"], str) or not event["id"]:
raise Core07ShapeError("id must be a non-empty string")
if not isinstance(event["who"], str) or not event["who"]:
raise Core07ShapeError("who must be a non-empty string")
if type(event["when"]) is not int:
raise Core07ShapeError("when must be an integer Unix time")

what = event["what"]
verb = event["verb"]
if verb == "D":
if not isinstance(what, Mapping) or "delegatee" not in what or "scope" not in what:
raise Core07ShapeError("D requires what.delegatee and what.scope")
elif verb == "T":
if "ref" not in event:
raise Core07ShapeError("T requires ref")
if not isinstance(what, Mapping) or "termination_scope" not in what:
raise Core07ShapeError("T requires what.termination_scope")
elif verb == "V":
if "ref" not in event:
raise Core07ShapeError("V requires ref")
if (
not isinstance(what, Mapping)
or "verification_scope" not in what
or "result" not in what
):
raise Core07ShapeError(
"V requires what.verification_scope and what.result"
)
48 changes: 48 additions & 0 deletions tests/test_core07_wire.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from jep_runtime.core.wire07 import Core07ShapeError, event_identity, validate_core07_shape


def _base(verb="J", what=None):
return {
"jep": "1",
"id": "urn:uuid:018f4f8d-7c63-7c2e-9b43-4ef657eec1c0",
"verb": verb,
"who": "did:example:alice",
"when": 1700000000,
"what": {"claim": "approve"} if what is None else what,
"sig": "detached-jws",
}


def test_event_identity_is_who_and_id():
event = _base()
assert event_identity(event) == (event["who"], event["id"])


def test_core_does_not_require_nonce():
event = _base()
validate_core07_shape(event)
assert "nonce" not in event


def test_delegation_minimum():
event = _base("D", {"delegatee": "did:example:bob", "scope": ["read"]})
validate_core07_shape(event)


def test_termination_requires_ref_and_scope():
event = _base("T", {"termination_scope": "future_reliance"})
try:
validate_core07_shape(event)
except Core07ShapeError as exc:
assert "T requires ref" in str(exc)
else:
raise AssertionError("expected Core07ShapeError")


def test_verification_requires_scope_result_and_ref():
event = _base("V", {"verification_scope": ["cryptographic"], "result": "PASS"})
event["ref"] = {
"type": "jep:event",
"value": {"who": "did:example:a", "id": "urn:uuid:x"},
}
validate_core07_shape(event)
Loading