Skip to content

Add POST /api/audit/append endpoint for instance-side wrapper audit integration #1174

Description

@dancingclaw

Upstream issue: Add POST /api/audit/append endpoint for instance-side wrapper audit integration

Repository: https://github.com/RightNow-AI/openfang
Affects: OpenFang v0.6.4 (SHA 3cce1eb3fb19ad590a0937e039a8bf8bc09aba13)
Filed by: assistant project / Phase 7 (personal-family pilot)
Decision provenance: D-A12 issue 5 (RESEARCH.md Finding 2 — audit-append gap)
Phase 3 carry-forward: RV-04 (audit chain integrity verified — but write-side endpoint missing)

Summary

The OpenFang OFP API surface exposes only read-side audit endpoints:

  • GET /api/audit/recent (crates/openfang-api/src/routes.rs:5027)
  • GET /api/audit/verify (crates/openfang-api/src/routes.rs:5063)

There is no write-side endpoint for external processes (instance-side
wrappers, sidecars, or out-of-band audit emitters) to append entries to
the Merkle chain. The append-side surface (AuditLog::record at
crates/openfang-runtime/src/audit.rs:180) is internal Rust API only.

This blocks instance-level wrappers (matrix sidecar, hand-reload,
skill-install gate) from contributing entries to the canonical audit
chain — they must run a parallel local Merkle chain instead, and
reconcile later. Phase 3 RESEARCH §Finding 2 surfaced this; the
personal-family pilot wraps it into D-A1.

Context

The personal-family pilot's instances/personal-family/wrappers/audit_glue
module runs a parallel JSONL Merkle chain at
instances/personal-family/state/audit-fallback.jsonl, mirroring the
upstream compute_entry_hash algorithm bit-for-bit (lines 61-79 of
audit.rs):

fn compute_entry_hash(seq, timestamp, agent_id, action, detail, outcome, prev_hash) -> String {
    let mut hasher = Sha256::new();
    hasher.update(seq.to_string().as_bytes());
    hasher.update(timestamp.as_bytes());
    hasher.update(agent_id.as_bytes());
    hasher.update(action.to_string().as_bytes());
    hasher.update(detail.as_bytes());
    hasher.update(outcome.as_bytes());
    hasher.update(prev_hash.as_bytes());
    hex::encode(hasher.finalize())
}

Once the upstream endpoint lands, the wrapper replays the fallback chain
into upstream and verifies hashes match — proving no integrity loss
during the workaround period.

Reproduction

# v0.6.4 has no append endpoint — POST returns 404
openfang start &
curl -fsS -X POST http://127.0.0.1:4200/api/audit/append \
    -H 'content-type: application/json' \
    -d '{"agent_id":"wrapper","action":"ConfigChange","detail":"HAND.toml reload sha256=abc","outcome":"ok"}'
# 404 Not Found

Proposed fix

Add POST /api/audit/append accepting JSON:

{ "agent_id":  "<string>",
  "action":    "ConfigChange|ToolInvoke|...",  // AuditAction enum
  "detail":    "<string>",
  "outcome":   "ok|deny|error" }

Returning:

{ "seq":  <u64>,
  "hash": "<sha256-hex>" }

Implementation: thin HTTP shim around AuditLog::record(...), behind
authn (re-use the session-auth middleware already present at
crates/openfang-api/src/session_auth.rs). Reject if the audit chain is
not initialized; rate-limit consistent with other write endpoints.

Affected upstream files

  • runtimes/openfang-agent/crates/openfang-api/src/routes.rs (add
    handler near the existing audit_recent / audit_verify pair around lines
    5027-5063)
  • runtimes/openfang-agent/crates/openfang-api/src/server.rs (route
    registration)
  • runtimes/openfang-agent/crates/openfang-runtime/src/audit.rs (line
    180 AuditLog::record — already exposes the needed Rust surface; no
    change required)

Workaround removal trigger

On upstream merge of POST /api/audit/append:

  1. Switch instances/personal-family/wrappers/src/audit_glue.rs from
    "try OFP, fall back to local chain" to "OFP only".
  2. Replay the on-disk state/audit-fallback.jsonl into the upstream
    audit log via the new endpoint, in seq order, asserting that each
    replayed hash matches the locally-recorded value.
  3. Drop the local chain file once replay verifies clean.

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-designNeeds architecture discussion

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions