Skip to content

Add mission log: agent-reported activity trail with hash-chained integrity #77

@rsharath

Description

@rsharath

Summary

Add a mission log — an append-only, hash-chained activity trail where agents report what they actually did, scoped to a delegation chain. This bridges the gap between what ZeroID knows an agent was authorized to do (tokens, scopes, delegation chains) and what it actually did (actions, outcomes, resources accessed).

Inspired by the mission log concept in draft-hardt-aauth-protocol and Karl McGuinness's analysis of its governance gaps. This proposal adapts the concept to ZeroID's existing architecture.

mission log entries could include the drm_hash and constraint_catalog_hash from the token that authorized the action, so a single log entry can be traced back to the policy version.

Background

What AAuth's mission log is

An ordered record of all agent↔Person Server interactions within a mission:

Entry type Who creates it When
Token requests PS records automatically Agent calls token_endpoint
Permission requests PS records automatically Agent calls permission_endpoint
Audit records Agent reports Agent calls audit_endpoint
Interaction requests PS records automatically Agent requests human interaction
Clarification exchanges PS records automatically During mission approval dialogue

The key design choice: the agent is a first-class contributor to its own audit trail. This has no equivalent in OAuth or in ZeroID today.

The AAuth spec intentionally leaves the log under-specified — no entry schema, no read API, no integrity mechanism.

What ZeroID already has

AAuth concept ZeroID equivalent Gap
Token request log issued_credentials — every token with grant_type, delegation_depth, parent_jti Exists
Security event log cae_signals — signal_type, severity, payload, identity_id Exists
Mission scope act chain + 3-way scope intersection on token_exchange Exists
PS evaluation OnClaimsIssue hook Plumbing exists
Agent-reported actions Nothing Gap
Hash-chained integrity Nothing Gap

Design

Mission identifier = mission_id (populated by #81)

Mission ID = mission_id. A mission is the tree of tokens rooted at an orchestrator's initial credential. The mission_id claim and column introduced by #81 (expose and denormalize mission_id for delegation-chain audit queries) is the mission identifier — no parallel workflow_id, correlation_id, or root_jti is introduced. The codebase has exactly one name for this concept.

Do not invent a new UUID for mission identity, and do not interpret mission_id as a JTI at the consumer layer — it is opaque by contract (see #81). The underlying scheme (currently: the root credential's JTI) is an implementation detail that may evolve.

Endpoint

POST /oauth2/mission/log
Authorization: Bearer <agent-token>
{
  "action":   "tool:execute",
  "resource": "github.com/api/repos/org/repo/pulls",
  "outcome":  "success",
  "detail":   {"tool": "create_pull_request", "pr_number": 42}
}

The server:

  1. Extracts the agent's JTI and mission_id from the bearer token (claim populated by feat: expose and denormalize mission_id for delegation-chain audit queries #81 — no chain walking required)
  2. Appends an entry with the next sequence number
  3. Computes entry_hash = SHA-256(prev_hash || entry_type || action || resource || outcome || detail || sequence || timestamp)
  4. Returns the entry with its hash

Schema

CREATE TABLE mission_log (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    mission_id  VARCHAR(255) NOT NULL,   -- mission identifier (see #81)
    agent_jti   VARCHAR(255) NOT NULL,   -- which credential reported this
    identity_id UUID REFERENCES identities(id) ON DELETE SET NULL,
    account_id  VARCHAR(255) NOT NULL,
    project_id  VARCHAR(255) NOT NULL,

    entry_type  VARCHAR(50)  NOT NULL,   -- action, decision, error
    action      VARCHAR(255),
    resource    TEXT,
    outcome     VARCHAR(50),
    detail      JSONB,

    sequence    BIGINT       NOT NULL,   -- ordering within mission
    prev_hash   VARCHAR(64),             -- SHA-256 of previous entry
    entry_hash  VARCHAR(64)  NOT NULL,   -- SHA-256 of this entry

    created_at  TIMESTAMPTZ  NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_mission_log_mission_id_seq ON mission_log (mission_id, sequence);
CREATE INDEX idx_mission_log_identity ON mission_log (identity_id);
CREATE INDEX idx_mission_log_tenant ON mission_log (account_id, project_id);

Read endpoint

GET /oauth2/mission/log?mission_id=<id>
Authorization: Bearer <agent-token>

Returns the ordered log for a delegation chain. Optionally filterable by identity_id, entry_type, action. Shape aligns with the #81 admin API (GET /credentials?mission_id=..., GET /signals?mission_id=...) so a single mission ID threads cleanly across mission log, credentials, and signals.

What this enables

1. Audit that answers "why" not just "what"

Today: agent B got a token with data:read scope delegated from agent A.
With mission log: agent B used that token to read 3 files, create a PR, and post a Slack message — and whether each action succeeded.

2. Policy decisions informed by history

The OnClaimsIssue hook (or a new OnTokenExchange hook) could receive the mission log as context. Example: "This sub-agent is requesting deploy:write scope, and the mission log shows 47 file reads and 0 test runs — deny until tests pass."

3. CAEP integration for trajectory drift

A monitoring layer reads the mission log, detects trajectory drift (many individually-authorized actions that collectively diverge from intent), and pushes a policy_violation CAEP signal back into ZeroID to revoke the chain. This closes the gap identified — runtime alignment monitoring that feeds back into the auth layer.

4. Tamper-evident compliance

The hash chain means any modification to a log entry invalidates all subsequent hashes. For EU AI Act traceability or SOC2 audit: cryptographic proof the log is intact, not just "here's what we logged."

What this does NOT require

  • No new token type or claim beyond what feat: expose and denormalize mission_id for delegation-chain audit queries #81 introduces (mission_id)
  • No new mission identifier — mission_id is THE mission identifier
  • No changes to existing grant types
  • No mission description or clarification channel (can come later)
  • No changes to forward-auth proxy or JWKS endpoints
  • Agents that don't report are unaffected — the log is opt-in

Dependencies

Scope

Minimal implementation: one migration, one domain model, one repo, one handler endpoint (POST + GET), and a hash function. Composes with existing infrastructure rather than replacing any of it.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions