Strategic work for IETF submission of draft-nennemann-act-01 and
draft-nennemann-wimse-ect-02:
Package restructure:
- move ACT and ECT refimpls to workspace/packages/{act,ect}/
- ietf-act and ietf-ect distribution names (sibling packages)
- cross-spec interop test plan (INTEROP-TEST-PLAN.md)
ACT draft -01 revisions:
- rename 'par' claim to 'pred' (align with ECT)
- rename 'Agent Compact Token' to 'Agent Context Token' (semantic
alignment with ECT family)
- add Applicability section (MCP, OpenAI, LangGraph, A2A, CrewAI)
- add DAG vs Linear Delegation Chains section (differentiator vs
txn-tokens-for-agents actchain, Agentic JWT, AIP/IBCTs)
- add Related Work: AIP, SentinelAgent, Agentic JWT, txn-tokens-for-agents,
HDP, SCITT-AI-agent-execution
- pin SCITT arch to -22, note AUTH48 status
Outreach drafts:
- Emirdag liaison email (SCITT-AI coordination)
- OAuth ML response on txn-tokens-for-agents-06
Strategy document:
- STRATEGY.md with phased action plan, risk register, timeline
Submodule:
- update workspace/drafts/ietf-wimse-ect pointer to -02 commit
98 lines
2.6 KiB
Python
98 lines
2.6 KiB
Python
"""Audit ledger per Section 9."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
from abc import ABC, abstractmethod
|
|
from dataclasses import dataclass
|
|
from typing import TYPE_CHECKING
|
|
|
|
from ect.types import Payload
|
|
|
|
if TYPE_CHECKING:
|
|
pass
|
|
|
|
|
|
class ErrTaskIDExists(Exception):
|
|
"""Raised when appending an ECT whose tid already exists."""
|
|
|
|
|
|
@dataclass
|
|
class LedgerEntry:
|
|
ledger_sequence: int
|
|
task_id: str
|
|
agent_id: str
|
|
action: str
|
|
predecessors: list[str]
|
|
ect_jws: str
|
|
signature_verified: bool
|
|
verification_timestamp: float
|
|
stored_timestamp: float
|
|
|
|
|
|
class Ledger(ABC):
|
|
"""Append-only audit ledger; lookup by task id (jti)."""
|
|
|
|
@abstractmethod
|
|
def append(self, ect_jws: str, payload: Payload) -> int:
|
|
"""Returns new ledger sequence number."""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_by_tid(self, tid: str) -> Payload | None:
|
|
pass
|
|
|
|
@abstractmethod
|
|
def contains(self, tid: str, wid: str) -> bool:
|
|
pass
|
|
|
|
|
|
class MemoryLedger(Ledger):
|
|
"""In-memory append-only ECT store implementing Ledger and ECTStore."""
|
|
|
|
def __init__(self) -> None:
|
|
self._seq = 0
|
|
self._by_tid: dict[str, "Payload"] = {}
|
|
self._entries: list[LedgerEntry] = []
|
|
self._lock = __import__("threading").Lock()
|
|
|
|
def append(self, ect_jws: str, payload: Payload) -> int:
|
|
if payload is None:
|
|
return 0
|
|
with self._lock:
|
|
wid = payload.wid or ""
|
|
if self._contains_locked(payload.jti, wid):
|
|
raise ErrTaskIDExists("ect: task ID (jti) already exists in ledger")
|
|
self._seq += 1
|
|
now = time.time()
|
|
entry = LedgerEntry(
|
|
ledger_sequence=self._seq,
|
|
task_id=payload.jti,
|
|
agent_id=payload.iss,
|
|
action=payload.exec_act,
|
|
predecessors=list(payload.pred) if payload.pred else [],
|
|
ect_jws=ect_jws,
|
|
signature_verified=True,
|
|
verification_timestamp=now,
|
|
stored_timestamp=now,
|
|
)
|
|
self._by_tid[payload.jti] = payload
|
|
self._entries.append(entry)
|
|
return self._seq
|
|
|
|
def get_by_tid(self, tid: str) -> Payload | None:
|
|
with self._lock:
|
|
return self._by_tid.get(tid)
|
|
|
|
def contains(self, tid: str, wid: str) -> bool:
|
|
with self._lock:
|
|
return self._contains_locked(tid, wid)
|
|
|
|
def _contains_locked(self, tid: str, wid: str) -> bool:
|
|
p = self._by_tid.get(tid)
|
|
if p is None:
|
|
return False
|
|
if not wid:
|
|
return True
|
|
return (p.wid or "") == wid
|