Overview

The audit logging system tracks all significant actions in the voting system for security, compliance, and troubleshooting purposes. All audit logs are stored in the database and can be viewed via the admin interface.

What Gets Logged

Logged Actions

The system logs the following actions:

  • election.created: When an election is created
  • election.deleted: When an election is deleted
  • election.closed: When an election is manually closed
  • election.viewed: When someone views an election page
  • tokens.generated: When tokens are generated for an election
  • vote.submitted: When a vote is successfully submitted
  • results.viewed: When election results are viewed
  • admin.access: When admin routes are accessed
  • token.invalid: When an invalid token is used
  • rate_limit.exceeded: When rate limits are exceeded

Logged Information

Each audit log entry includes:

  • ID: Unique identifier (UUID)
  • Action: Action type (see above)
  • IP Address: Client IP address (if available)
  • User Agent: Browser/client user agent (if available)
  • Election ID: Associated election (if applicable)
  • Timestamp: When the action occurred (Unix timestamp in milliseconds)
  • Metadata: Additional context (JSON string)

Database Schema

audit_logs Table

CREATE TABLE audit_logs (
    id TEXT PRIMARY KEY,              -- UUID
    action TEXT NOT NULL,             -- Action type
    ip_address TEXT,                  -- Client IP
    user_agent TEXT,                  -- User agent string
    election_id TEXT,                  -- Associated election
    timestamp INTEGER NOT NULL,        -- Unix timestamp (ms)
    metadata TEXT NOT NULL,            -- JSON string of additional data
    FOREIGN KEY (election_id) REFERENCES elections(id)
);

Indexes

  • Index on action (for filtering)
  • Index on election_id (for election-specific queries)
  • Index on timestamp (for time-based queries)

Metadata Structure

Election Created

{
  "election_id": "elec_123",
  "title": "Board President 2024",
  "ballot_type": "RANKED_CONDORCET"
}

Vote Submitted

{
  "election_id": "elec_123",
  "ballot_type": "SIMPLE_TRIPLE",
  "choice": "YES"
}

Note: Token information is never logged for security.

Admin Access

{
  "endpoint": "/admin/elections",
  "method": "GET",
  "action": "list_elections"
}

Rate Limit Exceeded

{
  "limit_type": "vote",
  "ip_address": "192.168.1.1",
  "endpoint": "/e/elec_123/vote"
}

Viewing Audit Logs

Via Admin UI

  1. Navigate to Admin → Audit Logs
  2. View logs in chronological order (newest first)
  3. Filter by:
    • Action: Select specific action type
    • Election: Filter by election ID
  4. Pagination: 100 logs per page

Via API

Endpoint: GET /admin/audit-logs

Query Parameters:

  • limit: Number of logs to return (default: 100)
  • offset: Pagination offset (default: 0)
  • action: Filter by action type
  • election_id: Filter by election ID

Response:

{
  "logs": [
    {
      "id": "log_abc123",
      "action": "vote.submitted",
      "ip_address": "192.168.1.1",
      "user_agent": "Mozilla/5.0...",
      "election_id": "elec_123",
      "timestamp": 1704067200000,
      "metadata": "{\"ballot_type\":\"SIMPLE_TRIPLE\"}"
    }
  ],
  "total": 150,
  "limit": 100,
  "offset": 0
}

Security Considerations

Privacy

  • IP Addresses: Logged for security but should be handled carefully
  • User Agents: May contain identifying information
  • No Tokens: Token information is never logged
  • No Personal Data: Email addresses not logged in metadata

Retention

  • No Automatic Deletion: Logs are kept indefinitely
  • Manual Cleanup: Can be deleted via database queries
  • Compliance: Consider retention policies for your jurisdiction

Access Control

  • Admin Only: Audit logs are only accessible to admins
  • Rate Limited: Audit log viewing is rate-limited (50 req/min)
  • No Public Access: Logs are never exposed to public

Creating Audit Logs

In Code

import { createAuditLog, AUDIT_ACTIONS } from "../utils/audit";
 
await createAuditLog(db, AUDIT_ACTIONS.VOTE_SUBMITTED, {
  ipAddress: c.req.header("CF-Connecting-IP"),
  userAgent: c.req.header("User-Agent"),
  electionId: election.id,
  metadata: {
    ballot_type: "SIMPLE_TRIPLE",
    choice: "YES"
  }
});

Error Handling

Audit logging never fails the request. If logging fails:

  • Error is logged to console
  • Request continues normally
  • No user-facing error

This ensures audit logging doesn’t impact system reliability.

Use Cases

Security Monitoring

Track suspicious activity:

  • Multiple invalid token attempts
  • Rate limit violations
  • Unusual access patterns

Compliance

Maintain audit trail for:

  • Regulatory compliance
  • Internal audits
  • Legal requirements

Troubleshooting

Debug issues:

  • Track when actions occurred
  • Identify problematic requests
  • Correlate errors with actions

Analytics

Analyze usage:

  • Election participation
  • Admin activity
  • System usage patterns

Querying Audit Logs

Common Queries

All votes for an election:

SELECT * FROM audit_logs 
WHERE action = 'vote.submitted' 
  AND election_id = 'elec_123'
ORDER BY timestamp DESC;

Admin access in last 24 hours:

SELECT * FROM audit_logs 
WHERE action = 'admin.access' 
  AND timestamp > (unixepoch() * 1000 - 86400000)
ORDER BY timestamp DESC;

Rate limit violations:

SELECT * FROM audit_logs 
WHERE action = 'rate_limit.exceeded'
ORDER BY timestamp DESC
LIMIT 100;

Invalid token attempts:

SELECT ip_address, COUNT(*) as attempts
FROM audit_logs 
WHERE action = 'token.invalid'
GROUP BY ip_address
ORDER BY attempts DESC;

Best Practices

For Administrators

  1. Regular Review: Periodically review audit logs
  2. Monitor Suspicious Activity: Watch for patterns
  3. Retention Policy: Establish log retention policy
  4. Access Control: Limit who can view audit logs

For Developers

  1. Log Important Actions: Log all significant actions
  2. Include Context: Add relevant metadata
  3. Never Log Secrets: Never log tokens, API keys, etc.
  4. Handle Errors Gracefully: Don’t fail requests if logging fails

Limitations

Current Limitations

  1. No Automatic Cleanup: Logs accumulate indefinitely
  2. No Export: Must query database directly
  3. No Real-Time Alerts: No automatic alerting on suspicious activity
  4. Limited Filtering: Basic filtering only

Future Enhancements

Potential improvements:

  • Automatic log rotation/archival
  • CSV/JSON export
  • Real-time alerting
  • Advanced filtering/search
  • Log aggregation/analytics

Performance Considerations

Indexing

Audit logs are indexed on:

  • action: For filtering by action type
  • election_id: For election-specific queries
  • timestamp: For time-based queries

Query Performance

  • Recent Logs: Fast (indexed by timestamp)
  • Filtered Queries: Fast (indexed columns)
  • Full Table Scans: Slow (avoid for large datasets)

Scaling

For high-volume systems:

  • Consider log rotation
  • Archive old logs
  • Use time-based partitioning
  • Implement log aggregation

Migration

Audit logs were added in migration 002_audit_logs.sql. The table is created automatically during migration.

See Also

  • ops - General infrastructure documentation
  • migrations - Database migration documentation
  • api-reference - API endpoint documentation