Receipts & Audit
Master ARIA Shield's Seven Controls, tamper-evident receipts, hash chain integrity, comprehensive audit logging, and complete your EmpowerNow Developer Academy journey.
๐Learning Objectives
- Understand ARIA Shield's Seven Controls framework
- Learn how tamper-evident receipts work
- Understand hash chains and integrity verification
- Use Plugin SDK telemetry for observability
- Query audit logs and receipts
- Complete your Week 5 and full program assessment
๐ก๏ธARIA Shield: Seven Controls
ARIA Shield implements seven security controls that wrap every platform operation:
Identity"] A1 --> A2["[2] AuthZ
Policy"] A2 --> A3["[3] Attest
Sign"] A3 --> EXEC["โก Execute"] EXEC --> A6["[6] Account
Actor Chain"] A6 --> A4["[4] Audit
Receipt"] A4 --> A5["[5] Anomaly
ML Detect"] A4 --> A7["[7] Archive
Immutable"] A5 -.->|"Alert"| SIEM["๐จ SIEM"] style A1 fill:#00d9ff22,stroke:#00d9ff style A2 fill:#a855f722,stroke:#a855f7 style A4 fill:#10b98122,stroke:#10b981 style A7 fill:#f59e0b22,stroke:#f59e0b
1. Authentication
Every request has verified identity (JWT, mTLS, DPoP)
2. Authorization
PDP evaluates every action against policy; fail-closed
3. Attestation
Cryptographic signature proves the decision was made
4. Audit
Complete trail of all operations with receipts
5. Anomaly Detection
ML-based behavioral analysis flags unusual patterns
6. Accountability
Traceable actor chains (user โ agent โ service)
7. Archive
Immutable, long-term storage with retention policies
๐งพTamper-Evident Receipts
Every sensitive operation generates a cryptographically signed receipt:
{
// Unique receipt identifier
"receipt_id": "rec_01HZW9X8Y7QKJN3M5P2R4T6V8W",
"timestamp": "2026-01-21T14:30:15.123Z",
// Who performed the action (full chain)
"actor": {
"user_id": "u:alice@corp.com",
"agent_id": "aria://bff/invoice-agent",
"delegation_id": "del_01HZW...",
"chain": ["u:alice@corp.com", "invoice-agent", "crud-service"]
},
// What action was performed
"action": {
"type": "workflow.execute",
"resource": "arn:aria:crud:workflow/approve_invoice",
"input_hash": "sha256:abc123..." // Hash of input, not input itself
},
// PDP decision details
"decision": {
"decision_id": "dec_01HZW...",
"effect": "Permit",
"eps_etag": "W/\"v42\"",
"policy_refs": ["invoices/approve", "global/audit"]
},
// Hash chain for integrity
"chain": {
"prev_hash": "sha256:9f86d08...",
"self_hash": "sha256:a1b2c3d...",
"sequence": 1520483
},
// Cryptographic signature
"signature": "eyJhbGciOiJFUzI1NiIs..."
}
Receipt Fields Explained
| Field | Purpose |
|---|---|
receipt_id |
Globally unique identifier (ULID format) |
actor.chain |
Full delegation chain from user to executing service |
action.input_hash |
Hash of input data (never stores sensitive data) |
decision.eps_etag |
Policy version used for the decision |
chain.prev_hash |
Hash of previous receipt (forms chain) |
chain.sequence |
Monotonic sequence number |
signature |
JWS signature over receipt (ES256) |
๐Hash Chain Integrity
prev: genesis
self: sha256:aaa..."] R2["Receipt #1520482
prev: sha256:aaa...
self: sha256:bbb..."] R3["Receipt #1520483
prev: sha256:bbb...
self: sha256:ccc..."] R4["Receipt #1520484
prev: sha256:ccc...
self: sha256:ddd..."] R1 --> R2 --> R3 --> R4 style R1 fill:#10b98122,stroke:#10b981 style R2 fill:#10b98122,stroke:#10b981 style R3 fill:#10b98122,stroke:#10b981 style R4 fill:#10b98122,stroke:#10b981
Verification Algorithm
// Verify a single receipt
function verifyReceipt(receipt, trustedPublicKey) {
// 1. Verify JWS signature
const payload = JSON.stringify({
receipt_id: receipt.receipt_id,
timestamp: receipt.timestamp,
actor: receipt.actor,
action: receipt.action,
decision: receipt.decision,
chain: receipt.chain
});
if (!verifyJWS(receipt.signature, payload, trustedPublicKey)) {
throw new Error('Invalid signature');
}
// 2. Verify self_hash matches content
const computedHash = sha256(payload);
if (computedHash !== receipt.chain.self_hash) {
throw new Error('Self hash mismatch');
}
return true;
}
// Verify chain continuity
function verifyChain(receipts) {
for (let i = 1; i < receipts.length; i++) {
const prev = receipts[i - 1];
const curr = receipts[i];
// Current's prev_hash must equal previous's self_hash
if (curr.chain.prev_hash !== prev.chain.self_hash) {
throw new Error(`Chain break at sequence ${curr.chain.sequence}`);
}
// Sequence must be monotonic
if (curr.chain.sequence !== prev.chain.sequence + 1) {
throw new Error(`Sequence gap at ${curr.chain.sequence}`);
}
}
return true;
}
If any receipt is modified, its self_hash changes, which breaks the prev_hash link in the next receipt. The entire chain from that point forward becomes invalid.
๐Plugin SDK Telemetry
Use sdk.telemetry to emit structured logs and metrics from your plugins:
const sdk = globalThis.pluginSdk;
// Structured logging
sdk.telemetry.logInfo('Invoice approved', {
invoice_id: 'INV-2026-001',
amount: 15000,
approver: 'alice@corp.com'
});
sdk.telemetry.logError('Approval failed', {
invoice_id: 'INV-2026-002',
error_code: 'BUDGET_EXCEEDED',
budget_remaining: 500
});
// Track business events
sdk.telemetry.trackEvent('invoice_approved', {
invoice_id: 'INV-2026-001',
workflow_id: 'wf_01HZW...',
duration_ms: 1250
});
// Measure performance
const timer = sdk.telemetry.startTimer('api_call');
const response = await sdk.api.fetch('/api/invoices');
timer.stop(); // Emits timing metric
// Or use measureTiming helper
const result = await sdk.telemetry.measureTiming('invoice_fetch', async () => {
return await sdk.api.fetch('/api/invoices');
});
Telemetry Best Practices
- Use structured data: Pass objects, not formatted strings
- Include correlation IDs: Link related events across services
- Avoid PII in logs: Use IDs, not names or emails
- Track business metrics: Count approvals, measure latency
- Use appropriate levels: Info for success, Error for failures
๐Querying Audit Logs
Receipts API
# Query receipts by actor
curl "https://bff.self.empowernow.ai/audit/receipts?actor=u:alice@corp.com&limit=50" \
-H "Authorization: Bearer $TOKEN"
# Query by resource
curl "https://bff.self.empowernow.ai/audit/receipts?resource=workflow/approve_invoice" \
-H "Authorization: Bearer $TOKEN"
# Query by time range
curl "https://bff.self.empowernow.ai/audit/receipts?from=2026-01-20T00:00:00Z&to=2026-01-21T23:59:59Z" \
-H "Authorization: Bearer $TOKEN"
# Verify chain integrity
curl "https://bff.self.empowernow.ai/audit/receipts/verify?from_seq=1520480&to_seq=1520490" \
-H "Authorization: Bearer $TOKEN"
Response Example
{
"receipts": [
{ "receipt_id": "rec_01HZW...", "timestamp": "...", ... },
{ "receipt_id": "rec_01HZX...", "timestamp": "...", ... }
],
"pagination": {
"total": 127,
"limit": 50,
"offset": 0,
"has_more": true
},
"chain_status": {
"verified": true,
"from_sequence": 1520480,
"to_sequence": 1520529
}
}
Data Retention
| Data Type | Hot Storage | Cold Archive |
|---|---|---|
| Authorization Receipts | 90 days (ClickHouse) | 7 years (S3 Glacier) |
| Workflow Receipts | 90 days | 7 years |
| Telemetry Events | 30 days | 1 year |
| Anomaly Alerts | 180 days | 7 years |
โExternal Anchoring
Anchoring periodically commits the latest chain head hash to an external, immutable store. This proves the chain existed at a specific time, even if the primary storage is compromised.
Anchor Flow
modification/deletion
Anchor Record
{
"anchor_id": "anc_01HZW...",
"ts": "2026-01-21T15:00:00Z",
"chain_head": "sha256:d4e5f6...",
"sequence_range": {
"from": 1520000,
"to": 1520999
},
"store": "s3://aria-receipts-prod/heads/latest",
"signature": "eyJhbGciOiJFUzI1NiIs..."
}
Anchor Verification
# Verify that receipts 1520000-1520999 haven't been tampered
curl "https://bff.self.empowernow.ai/audit/receipts/verify-anchor" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"anchor_id": "anc_01HZW...",
"include_receipts": false
}'
# Response
{
"verified": true,
"chain_valid": true,
"anchor_timestamp": "2026-01-21T15:00:00Z",
"external_store_verified": true
}
Supported external stores: S3 Object Lock (WORM), Azure Immutable Blob, GCS Bucket Lock, and third-party notary services (e.g., timestamping authorities).
๐Visual Diffs
Receipts include fingerprints that enable human-readable diffs for audit and investigation.
Types of Diffs
| Diff Type | What It Shows |
|---|---|
| Plan Diff | Planned vs executed steps; added/removed/modified params |
| Data Diff | Before/after state of modified records (JSON or table) |
| Policy Diff | Intended vs actual constraints; budget consumed |
Example: Plan Diff
// What was planned
{
"steps": [
{ "kind": "workflow", "ref": "create_user", "params": { "email": "jane@corp.com" } },
{ "kind": "workflow", "ref": "add_to_group", "params": { "group": "employees" } }
]
}
// What actually executed (step 2 skipped due to deny)
{
"steps": [
{ "kind": "workflow", "ref": "create_user", "params": { "email": "jane@corp.com" }, "status": "completed" },
{ "kind": "workflow", "ref": "add_to_group", "params": { "group": "employees" }, "status": "denied", "reason": "policy: no_external_groups" }
]
}
The Receipts UI integrates with Grafana for timeline visualization. Each receipt is a point on the timeline with clickable per-step diffs.
๐๏ธStorage Architecture
Search Index] DW[S3 / WORM
Immutable Store] ANCHOR[Anchor Journal] end PEP --> Q Q --> IDX Q --> DW DW --> ANCHOR Analysts[Sec/FinOps UI] --> IDX Forensics[IR/Legal Export] --> DW style IDX fill:#00d9ff22,stroke:#00d9ff style DW fill:#10b98122,stroke:#10b981 style ANCHOR fill:#f59e0b22,stroke:#f59e0b
Storage Tiers
- Hot (ClickHouse): Fast query for recent receipts (90 days). Indexed by actor, tool, decision, timestamp.
- Cold (S3 WORM): Append-only immutable storage for compliance (7 years). Write-once, read-many.
- Anchor (S3 Object Lock): Periodic chain head commits with version locking. Third-party verifiable.
๐Week 5 Review
Day 21: MCP & Tools
- MCP primitives (Tools, Resources, Prompts)
- Loopback MCP & tool naming
- MCP Gateway authorization
- Progressive tool discovery
Day 22: AI & LLM
- Multi-provider LLM support
- Dynamic model routing
- Budget enforcement (402)
- Streaming responses
Day 23: ARIA Framework
- AI agent delegation model
- Trust levels & capabilities
- Chain eligibility validation
- PDP integration for agents
Day 24: NowConnect
- Outbound-only tunneling
- Identity-anchored sessions
- WebSocket multiplexing
- Connector configuration
โKnowledge Check
Each receipt contains: (1) a self_hash computed from its content, (2) a prev_hash linking to the previous receipt's self_hash, and (3) a JWS signature over the entire payload. Modifying any field changes the hash, breaking the chain link and invalidating the signature.
1. Authentication (verify identity), 2. Authorization (PDP policy), 3. Attestation (cryptographic proof), 4. Audit (receipt trail), 5. Anomaly Detection (ML-based), 6. Accountability (actor chains), 7. Archive (immutable storage).
input_hash instead of the actual input?
โผ
Receipts are stored long-term and may be accessed for compliance audits. Storing actual input data could expose sensitive information. The hash proves the input existed and hasn't changed, without revealing the data itself. If needed for investigation, the original input can be retrieved from operational logs (with appropriate access controls).
actor.chain field used for?
โผ
The actor.chain captures the full delegation path: who initiated the action, which agents acted on their behalf, and which service ultimately executed it. This enables accountabilityโyou can trace any action back to the human who authorized it, even through multiple layers of AI agents.
Call the chain verification API: /audit/receipts/verify?from_seq=X&to_seq=Y. It will: (1) verify each receipt's JWS signature against the trusted key, (2) recompute each self_hash and compare, (3) verify each prev_hash matches the previous receipt's self_hash. Any tampering will cause verification to fail at the modified receipt.
๐Day 25 Checkpoint
- Understand ARIA Shield's Seven Controls
- Know how tamper-evident receipts work
- Understand hash chain integrity verification
- Can use sdk.telemetry for observability
- Can query audit logs and receipts
- Know data retention policies