Skip to content

AuditEnvelope v1 — unified abstract audit message format (Phases B + C + F) #97

@hanwencheng

Description

@hanwencheng

Context

PR #95 (issue #82 ERC-7730 + EIP-712 sign) added signed_intent_text + signed_intent_hash to the audit-row schema, but only for typed-data signs. The rest of the audit surface (scope mutations, device mutations, payments, memory ops, email, K3 epoch advance) still carries the narrow (actor_omni, service_hash, op_type ∈ {0,1,2}, payload_hash) shape that CredentialAudit.sol takes — useless for sign events and orthogonal data classes.

Phase A — the canonical schema + non-break design — landed in PR #95 under arch.md §15.3a. This issue tracks the implementation phases B / C / F.

Phase B — worker + core migration

  • Add AuditEnvelope v1 struct to agentkeys-core (CBOR-serializable, deterministic encoding per RFC 8949 §4.2.1).
  • Add AuditOpKind u8 + per-kind op_body schemas matching the canonical table in arch.md §15.3a.
  • Add GET /v1/audit/envelope/<hash> endpoint to `agentkeys-worker-audit` returning the full envelope.
  • Migrate the existing `POST /v1/audit/append` to accept the new envelope shape; keep the old shape on `/v1/audit/append/v1` for one cycle so older callers don't break.
  • Update credentials-service + memory-service workers to emit `AuditEnvelope` instead of the legacy 5-field struct.
  • Compute `envelope_hash = keccak256(canonical_cbor(envelope))` for chain commitment.
  • Test: CBOR encode + decode roundtrip on canonical fixtures per op_kind.

Phase C — contract revision

  • Add `appendV2(operatorOmni, actorOmni, opKind, envelopeHash)` to `CredentialAudit.sol` (additive, retain v1).
  • Add `appendRootV2(operatorOmni, merkleRoot, opKindBitmap, entryCount)` with `opKindBitmap: bytes32` (each bit = one of 256 possible op_kinds present in the batch — explorers filter without fetching leaves).
  • Emit `AuditAppendedV2` + `AuditRootAppendedV2` events with `indexed opKind` topic for fast `eth_getLogs` filtering.
  • Forge tests covering both shapes coexist.
  • Redeploy on Heima Mainnet via `scripts/heima-bring-up.sh` (idempotent).
  • Update `docs/spec/deployed-contracts.md` with new address.

Phase F — extend op_kind coverage

Each row below is a separate PR that touches the arch.md table exactly once (claims a byte) + adds the worker emit-site + the explorer renderer in litentry/subscan-essentials + litentry/subscan-essentials-ui-react.

  • `SignEip191` (byte 20) — signer emits via daemon callback after `POST /dev/sign-message`.
  • `SignEip712` (byte 21) — signer emits via daemon callback after `POST /dev/sign-typed-data`. Carries the `intent_text` + `intent_commitment` from PR issue #82: ERC-7730 clear-signing + EIP-712 typed-data sign (v2-aligned) #95.
  • `ScopeGrant` (byte 40) + `ScopeRevoke` (byte 41) — broker emits when `AgentKeysScope.setScopeWithWebauthn` / `revokeScope` lands.
  • `DeviceAdd` (50) + `DeviceRevoke` (51) + `K10Rotate` (52) — SidecarRegistry hook emits.
  • `PaymentEscrowRedeem` (30) + `PaymentDirect` (31) — payment-service worker emits (requires §15.5 work).
  • `MemoryPut` (10) + `MemoryGet` (11) + `MemoryTeardown` (12) — memory-service worker emits.
  • `EmailSend` (60) + `EmailReceive` (61) — email-service worker emits.
  • `K3EpochAdvance` (70) — K3EpochCounter hook emits.

Non-break invariants (per arch.md §15.3a)

Every PR landing a new op_kind MUST preserve all 8 invariants:

  1. `op_kind` is `u8` not a sealed enum.
  2. Envelope-level fields are stable; only `op_body` is op-kind-specific.
  3. `version` bumps only on envelope-level changes, never on new op_kinds.
  4. Explorer ships a generic `Unknown(byte)` fallback renderer.
  5. Worker passes through opaque `op_body` bytes when it doesn't recognize them.
  6. Chain contract is op-kind-agnostic.
  7. Canonical op_kind table in arch.md — PRs MUST append a row; numbers never reused.
  8. Each PR adds 3 tests: worker CBOR roundtrip + explorer fallback render + arch.md row.

Companion issue

Explorer-side work tracked at litentry/subscan-essentials (see linked issue below once it lands).

Acceptance

  • Phase B + C ship; Phase F has ≥3 op_kinds wired end-to-end (SignEip712, ScopeGrant, DeviceAdd recommended as the first three since they exercise typed-data + master mutations + registry).
  • `harness/v2-stage1-demo.sh` + `v2-stage2-demo.sh` + `v2-stage3-demo.sh` all green.
  • Explorer can render at least the three wired op_kinds; the others render with the `Unknown(byte)` fallback.

Related: #82 (closed by #95), #90 (Stage 2 hardening), #91 (worker hardening).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions