Blockchain Audit Trails¶
Kiket anchors audit logs to the Polygon blockchain for tamper-proof verification. This provides cryptographic proof that your audit records have not been modified since creation.
How It Works¶
- Content Hashing: Every audit record is SHA-256 hashed at creation time
- Merkle Trees: Hashes are collected into Merkle trees at regular intervals
- Blockchain Anchoring: Merkle roots are submitted to Polygon
- Verification: Anyone can verify a record's inclusion without accessing your data
┌─────────────────────────────────────────────────────────┐
│ Merkle Root │
│ (anchored to Polygon) │
├───────────────────────┬─────────────────────────────────┤
│ Hash AB │ Hash CD │
├───────────┬───────────┼───────────┬─────────────────────┤
│ Hash A │ Hash B │ Hash C │ Hash D │
│ Record 1 │ Record 2 │ Record 3 │ Record 4 │
└───────────┴───────────┴───────────┴─────────────────────┘
Benefits¶
- Tamper-Proof: Blockchain immutability prevents record modification
- Independent Verification: Auditors verify proofs without Kiket access
- Public Transparency: Transaction history viewable on PolygonScan
- Compliance Ready: SOC 2, ISO 27001, and GDPR compliant audit trails
What Gets Anchored¶
Issue Lifecycle¶
- Issue creation, updates, and deletion
- Status transitions and workflow changes
- Comment additions and modifications
- Attachment uploads
AI Operations¶
- AI agent invocations and decisions
- Auto-assignment recommendations
- Generated content and suggestions
- Model performance metrics
Configuration Changes¶
- Workflow definition updates
- Extension installations and settings
- Project settings modifications
- Organization-wide configuration
Security Events¶
- User permission changes
- API key management
- OAuth connection changes
- GitHub sync operations
Notification Dispatches¶
- Email notification dispatches
- Slack notification dispatches
- Webhook notification dispatches
- Links to source audit logs for transitive verification
All three audit models (AuditLog, AIAuditLog, NotificationAuditLog) compute SHA-256 content hashes at creation time and are included in the Merkle tree anchoring.
Anchoring Frequency¶
| Plan | Standard | High-Frequency | Immediate |
|---|---|---|---|
| Starter | Hourly | -- | -- |
| Professional | Hourly | 15 minutes | -- |
| Enterprise | Hourly | 15 minutes | Configurable |
High-Frequency Anchoring (Professional+)¶
Organizations with compliance requirements can enable 15-minute anchoring intervals for faster verification times.
Immediate Anchoring (Enterprise)¶
Critical actions can trigger immediate anchoring (within a 5-minute batched window):
- Role changes (user permissions modified)
- Approval decisions (granted or denied)
- API key creation/revocation
- Secret management operations
Viewing Anchors¶
Admin Dashboard¶
Organization admins can view blockchain anchoring status at /blockchain:
- Total Anchors -- Number of Merkle roots submitted
- Confirmed -- Successfully verified on-chain
- Pending -- Awaiting blockchain confirmation
- Failed -- Needs retry (automatic)
Activity Log¶
Users can view their organization's audit trail at /audit_trail:
- Filter by action type, resource, or date range
- See blockchain verification status for each action
- Export personal audit history
Record Badges¶
Throughout the application, look for blockchain status indicators:
- Verified (green chain icon) -- Record is blockchain-anchored
- Pending (yellow clock icon) -- Awaiting next anchor batch
CLI Commands¶
# List recent anchors
kiket audit anchors
# Filter by status
kiket audit anchors --status=confirmed
# JSON output
kiket audit anchors --format=json
# Check audit status
kiket audit status
Verifying Records¶
Verification confirms that:
- A record's content hash exists in a Merkle tree
- That Merkle root was submitted to the Polygon blockchain
- The transaction was confirmed with sufficient block confirmations
- The record content matches its original hash
Public Verification Page¶
Visit kiket.dev/verify to verify any proof without logging in.
Web Interface¶
Visit /audit_trail/verify to use the verification tool:
- By Content Hash -- Enter the SHA-256 hash of the record content
- By Record Reference -- Select the record type and ID
API Verification¶
curl -X POST https://api.kiket.dev/v1/audit/verify \
-H "Content-Type: application/json" \
-d '{
"content_hash": "0x7f83b165...",
"merkle_root": "0x3a4e8c2b...",
"proof": ["0x1234...", "0x5678..."],
"leaf_index": 127,
"tx_hash": "0x9a8b7c6d..."
}'
Response:
{
"verified": true,
"anchor": {
"id": 123,
"merkle_root": "0xdef456...",
"tx_hash": "0x789abc...",
"block_number": 12345678,
"confirmed_at": "2026-01-15T10:30:00Z",
"network": "polygon_mainnet",
"explorer_url": "https://polygonscan.com/tx/0x789abc..."
},
"proof": {
"leaf_index": 42,
"proof_path": ["0xaaa...", "0xbbb...", "0xccc..."]
}
}
CLI Verification¶
# Verify a proof file
kiket audit verify proof.json
# Verify locally (offline)
kiket audit verify proof.json --local
# Get proof for an AuditLog record (default)
kiket audit proof 12345 --format=file --output=proof.json
# Get proof for an AIAuditLog record
kiket audit proof 12345 --type=AIAuditLog --format=file --output=proof.json
SDK Verification¶
from kiket_sdk.audit import AuditClient
audit = AuditClient(client)
# Get proof for an AuditLog record
proof = await audit.get_proof(record_id=12345)
# Get proof for an AIAuditLog record
ai_proof = await audit.get_proof(record_id=67890, record_type="AIAuditLog")
# Verify via API
result = await audit.verify(proof)
print(f"Verified: {result.verified}")
# Verify locally (offline)
valid = AuditClient.verify_proof_locally(
content_hash=proof.content_hash,
proof_path=proof.proof,
leaf_index=proof.leaf_index,
merkle_root=proof.merkle_root
)
import { AuditClient } from '@kiket/sdk';
const audit = new AuditClient(httpClient);
// Get proof for an AuditLog record
const proof = await audit.getProof(12345);
// Get proof for an AIAuditLog record
const aiProof = await audit.getProof(67890, 'AIAuditLog');
// Verify via API
const result = await audit.verify(proof);
console.log(`Verified: ${result.verified}`);
// Verify locally (offline)
const valid = AuditClient.verifyProofLocally(
proof.content_hash,
proof.proof,
proof.leaf_index,
proof.merkle_root
);
audit = KiketSdk::AuditClient.new(client)
# Get proof for an AuditLog record
proof = audit.get_proof(12345)
# Get proof for an AIAuditLog record
ai_proof = audit.get_proof(67890, record_type: "AIAuditLog")
# Verify via API
result = audit.verify(proof)
puts "Verified: #{result.verified}"
# Verify locally (offline)
valid = KiketSdk::AuditClient.verify_proof_locally(
content_hash: proof.content_hash,
proof_path: proof.proof,
leaf_index: proof.leaf_index,
merkle_root: proof.merkle_root
)
Independent Verification¶
You can verify proofs independently without trusting Kiket:
- Get the proof data from the API or compliance export
- Recompute the Merkle root using the content hash and proof path
- Check the blockchain for the anchor transaction
- Verify the Merkle root in the transaction matches your computation
// Example: Independent Merkle proof verification
const crypto = require('crypto');
function verifyProof(contentHash, proofPath, expectedRoot) {
let current = contentHash;
for (const sibling of proofPath) {
// Sort hashes for consistent ordering
const pair = [current, sibling].sort();
current = crypto
.createHash('sha256')
.update(Buffer.from(pair[0] + pair[1], 'hex'))
.digest('hex');
}
return current === expectedRoot;
}
Understanding Verification Results¶
| Result | Meaning |
|---|---|
| Verified | Record is confirmed on the blockchain with anchor ID, Merkle root, transaction hash, block number, and explorer link |
| Not Found | Record hasn't been anchored yet, invalid hash format, or record doesn't exist |
| Verification Failed | Merkle proof doesn't match -- contact support if unexpected |
Verification for Compliance¶
For compliance audits, you may need to demonstrate:
- Chain of custody -- Export the full audit trail with proofs
- Independent verification -- Auditors can verify proofs themselves
- Blockchain transparency -- All transactions are public on Polygon
See Compliance Reports for generating exportable reports with embedded proofs.
Notification Verification¶
Kiket includes blockchain verification links in notification emails, allowing recipients to independently verify that a notification was legitimately sent by the platform.
How It Works¶
- When an email notification is sent, a
NotificationAuditLogis created - The audit log includes a reference to the source action that triggered the notification
- A verification URL is included in the email footer
- Recipients can click the link to verify the notification's authenticity
Email Footer¶
All blockchain-verified emails include a footer:
Transitive Verification¶
Notification audit logs reference their source audit log, enabling transitive verification:
- Verify the notification was sent (NotificationAuditLog)
- Click through to verify the action that triggered it (AuditLog)
This proves not only that the notification was sent, but also that the underlying action actually occurred.
Extension Webhook Payloads¶
Extension webhooks automatically receive audit context when available:
{
"event": "workflow.after_transition",
"issue": { ... },
"audit_context": {
"audit_log_hash": "0x7f83b1657ff1fc53b92dc18...",
"verification_url": "https://kiket.dev/verify/0x7f83b165...",
"blockchain_status": "pending"
}
}
Network Configuration¶
| Environment | Network | Chain ID | Explorer |
|---|---|---|---|
| Development | Polygon Amoy (testnet) | 80002 | amoy.polygonscan.com |
| Production | Polygon Mainnet | 137 | polygonscan.com |
Pricing¶
Blockchain anchoring is included in Starter and above plans:
| Plan | Monthly Anchors | Overage |
|---|---|---|
| Free | Not included | - |
| Starter | 100 | $0.10/anchor |
| Professional | 1,000 | $0.08/anchor |
| Enterprise | Unlimited | - |
Smart Contract¶
The AuditAnchor contract is deployed at:
- Polygon Amoy:
0x...(testnet) - Polygon Mainnet:
0x...(production)
Contract source is available in the kiket repository.
Troubleshooting¶
"Record not found"¶
- Ensure the content hash is correctly formatted (64 hex characters)
- Check if the record type and ID are valid
- The record may be pending anchoring
"Anchor not confirmed"¶
- The transaction is submitted but awaiting confirmations
- Wait 5-10 minutes and retry
- Check network status on Polygonscan
"Verification failed"¶
- Contact support -- this indicates a potential data integrity issue
- Provide the content hash and anchor ID for investigation
FAQ¶
How often are records anchored?¶
Records are batched and anchored every hour. Each anchor can contain up to 10,000 records.
Can I verify without Kiket access?¶
Yes. The proof contains all information needed for independent verification. Use the public verification page or verify locally with the SDK.
What happens if anchoring fails?¶
Failed anchors are retried automatically. If an anchor fails 3 times, it's marked as failed and an alert is sent.
Is my data visible on the blockchain?¶
No. Only cryptographic hashes (Merkle roots) are stored on-chain. The actual audit content remains private in Kiket.
Related¶
- Blockchain API -- Programmatic access to anchoring data
- Compliance Reports -- Generate audit reports with proofs
- EU AI Act Compliance -- AI-specific audit requirements