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:
- Switch
instances/personal-family/wrappers/src/audit_glue.rs from
"try OFP, fall back to local chain" to "OFP only".
- 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.
- Drop the local chain file once replay verifies clean.
Upstream issue: Add
POST /api/audit/appendendpoint for instance-side wrapper audit integrationRepository: 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::recordatcrates/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_gluemodule runs a parallel JSONL Merkle chain at
instances/personal-family/state/audit-fallback.jsonl, mirroring theupstream
compute_entry_hashalgorithm bit-for-bit (lines 61-79 ofaudit.rs):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
Proposed fix
Add
POST /api/audit/appendaccepting JSON:Returning:
Implementation: thin HTTP shim around
AuditLog::record(...), behindauthn (re-use the session-auth middleware already present at
crates/openfang-api/src/session_auth.rs). Reject if the audit chain isnot initialized; rate-limit consistent with other write endpoints.
Affected upstream files
runtimes/openfang-agent/crates/openfang-api/src/routes.rs(addhandler near the existing audit_recent / audit_verify pair around lines
5027-5063)
runtimes/openfang-agent/crates/openfang-api/src/server.rs(routeregistration)
runtimes/openfang-agent/crates/openfang-runtime/src/audit.rs(line180
AuditLog::record— already exposes the needed Rust surface; nochange required)
Workaround removal trigger
On upstream merge of
POST /api/audit/append:instances/personal-family/wrappers/src/audit_glue.rsfrom"try OFP, fall back to local chain" to "OFP only".
state/audit-fallback.jsonlinto the upstreamaudit log via the new endpoint, in seq order, asserting that each
replayed
hashmatches the locally-recorded value.