# Septor — Immutable Audit Trail

## What this skill covers
Writing audit events, reading the audit trail, what operations MUST be Septor-wired, and chain integrity verification.

## SDK Setup

```typescript
import { Septor } from '@insureco/septor'

const septor = new Septor({
  apiUrl: process.env.SEPTOR_URL,
  namespace: process.env.SERVICE_NAME || 'my-service',
})
```

Declare septor as an `internalDependency` to auto-inject `SEPTOR_URL`. Port defaults to 3000 and can be omitted:

```yaml
spec:
  internalDependencies:
    - service: septor   # creates SEPTOR_URL env var
```

## Writing Events

```typescript
// CORRECT: fire-and-forget with error logging — NEVER await on the request path
septor.emit('payment.created', {
  entityId: orgSlug,          // who/what this event concerns (index key)
  data: {
    amount: 5000,
    currency: 'USD',
    referenceId: paymentId,
  },
  metadata: {
    who: userId,              // the actor (user ID or service name)
    why: 'User initiated premium payment',
  },
}).catch((err) => logger.error({ err }, 'Septor emit failed — audit event lost'))

// WRONG: blocking your response on an audit write
await septor.emit(...)  // never do this
```

A Septor outage must never break your service. Always fire-and-forget with `.catch()`.

## What MUST Be Septor-Wired

Every financial, compliance, and authorization event is mandatory:

| Category | Required Event Types |
|----------|---------------------|
| Payments | `payment.created`, `payment.completed`, `payment.failed`, `payment.refunded` |
| Policies | `policy.bound`, `policy.endorsed`, `policy.cancelled`, `policy.renewed` |
| Compliance | `ofac.cleared`, `ofac.flagged`, `kyc.verified`, `kyc.failed` |
| Authorization | `user.login`, `permission.granted`, `permission.revoked` |
| Data Access | `record.accessed`, `report.exported`, `data.modified` |

Not required (platform handles automatically):
- Deploy events (iec-builder writes these)
- Gas events (Janus writes these)
- Credential rotations (builder writes these)

## Event Naming Convention

Use dot-separated `{resource}.{action}` format, lowercase:

```
payment.created       ✓
policy.bound          ✓
ofac.screen.cleared   ✓  (nested resource.sub.action is OK)

payment-created       ✗  (use dots, not hyphens)
createPayment         ✗  (use resource.action, not camelCase)
PAYMENT_CREATED       ✗  (lowercase only)
```

## Reading Audit Trails

```typescript
const { data } = await septor.query({
  entityId: orgSlug,
  eventType: 'payment.created',  // optional filter
  from: '2024-01-01',
  to: '2024-01-31',
  limit: '100',
})

for (const event of data.events) {
  console.log(event.eventType, event.metadata.who, event.data, event.chainIndex)
}
```

## Verifying Chain Integrity

```typescript
// Each event is cryptographically linked to the previous one
// Run this in background jobs, not request handlers — it's O(n)
const { data } = await septor.verify(orgSlug)

if (!data.valid) {
  logger.error({ entityId: orgSlug, brokenAt: data.brokenAt }, 'Audit chain broken!')
}
```

## Key Facts
- Each event gets a cryptographic hash linked to the previous event — the chain is tamper-evident
- `entityId` is the primary index — use a stable identifier like `orgSlug` or `userId`
- Events are immutable — once written, they cannot be modified or deleted
- Namespace scoping means each service sees only its own events

## Common Mistakes
- Awaiting `septor.emit()` on the request path — always fire-and-forget with `.catch()`
- Not logging the `.catch()` error — silent failures mean compliance gaps with no alert
- Using vague event types like `event.happened` — be specific: `payment.refunded`
- Forgetting `internalDependencies` for septor — `SEPTOR_URL` won't be injected
- Using inconsistent `entityId` for the same entity — pick one stable ID and stick to it
- Omitting `metadata.who` — compliance audits require knowing the actor
