← Back to Skills

Audit Trail on Tawa

import { Septor } from '@insureco/septor'

Ruleconfigure

About

import { Septor } from '@insureco/septor'

Skill Content

This is the raw markdown that gets installed as a Claude Code rule.

# Audit Trail on Tawa

## The Short Version

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

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

// Fire-and-forget — NEVER await septor on the critical path
septor.emit('payment.created', {
  entityId: orgSlug,
  data: { amount, referenceId: paymentId },
  metadata: { who: userId },
}).catch((err) => logger.error({ err }, 'Septor emit failed — audit event lost'))
```

## Setup

**Declare nothing.** `SEPTOR_URL` is auto-injected on every deploy, and since
iec-builder [`9e0a2d0`](https://git.tawa.pro) (2026-06-22) the builder also applies the
`tawa.pro/direct-dep.septor=true` NetworkPolicy label on **every** deploy, whether or
not you declare septor. The same holds for `iec-queue`.

In the builder, `ALWAYS_DIRECT_DEP_SERVICES = ['septor', 'iec-queue']` is unioned into
the namespace's direct-dep labels before the catalog is even consulted, so
`computeDirectDepServices(undefined)` still returns both.

If you declare septor anyway, the builder warns and **ignores the entry**:

> `septor is an auto-injected platform service — SEPTOR_URL and its NetworkPolicy
> direct-dep label are applied on every deploy. Declaring it in spec.dependencies is
> harmless but unnecessary; the entry will be ignored.`

That warning is correct — act on it.

> **Superseded guidance.** Before `9e0a2d0` this page said you MUST declare septor in
> `internalDependencies` or lose the audit trail, and told you to ignore the builder's
> warning. That was true until 2026-06-22 and is now wrong on both counts. The
> declaration is dead weight.
>
> **One caveat:** the label is applied *at deploy time*. A service that never declared
> septor and has not deployed since 2026-06-22 does not carry the label yet — its next
> deploy adds it.

## Authentication (REQUIRED — SDK ≥ 1.4.0)

Septor verifies a **Bio-ID token** on every emit/query. The SDK (≥ 1.4.0) handles
this automatically: when no `apiKey` is passed, it mints a `client_credentials`
token from `BIO_CLIENT_ID` / `BIO_CLIENT_SECRET` / `BIO_ID_URL` and refreshes it
ahead of expiry. **No per-app token code is needed.**

**This means your service MUST have Bio-ID credentials.** Declare `spec.auth` in
`catalog-info.yaml` so the builder injects `BIO_CLIENT_ID` / `BIO_CLIENT_SECRET`:

```yaml
spec:
  auth:
    mode: sso            # or: service-only (no user login, creds still injected)
```

Without `spec.auth`, those vars are absent and **every Septor emit fails with
`Authorization header required`** (the audit trail is silently lost since emits
are fire-and-forget). See https://tawa.insureco.io/reference/convention-septor

> Pin `@insureco/septor` to `^1.4.0`. Earlier versions do not authenticate and
> will be rejected by Septor.

## Fire-and-Forget (REQUIRED)

A Septor outage must NEVER break your service. Always fire-and-forget:

```typescript
// ✅ CORRECT
septor.emit('payment.created', { ... })
  .catch((err) => logger.error({ err }, 'Septor emit failed — audit event lost'))

// ❌ WRONG: blocking your user on an audit write
await septor.emit('payment.created', { ... })
```

## What MUST Be Septor-Wired

| Category | Required Events |
|----------|----------------|
| 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` |

**Automatically written by the platform (no code needed):**
- Deploy events (builder writes these)
- Gas events (Janus writes these)
- Job/cron events (iec-queue/iec-cron write these)
- Credential rotations (builder writes these)

## Event Naming

Use `{resource}.{action}` dot notation, lowercase:

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

payment-created       ❌  (hyphens — use dots)
createPayment         ❌  (camelCase)
PAYMENT_CREATED       ❌  (uppercase)
```

## Querying the Audit Trail

```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.createdAt, event.metadata.who)
}
```

## Verifying Chain Integrity

```typescript
// Run in background jobs, not request handlers — this is O(n)
const { data } = await septor.verify(orgSlug)
if (!data.valid) {
  logger.error({ brokenAt: data.brokenAt }, 'Audit chain broken!')
}
```

## Key Facts

- Each event gets a cryptographic hash linked to the previous — tamper-evident chain
- `entityId` is the primary index — use a stable identifier (`orgSlug` or `userId`)
- Events are immutable — once written, cannot be modified or deleted
- Each service sees only its own events (namespace scoping)
- Missing `.catch()` is a compliance gap — failures must be logged

## What NOT to Do

```typescript
// ❌ WRONG: blocking on audit write
await septor.emit(...)

// ❌ WRONG: silent failure
septor.emit(...).catch(() => {})  // swallows the error — compliance gap

// ❌ WRONG: vague event type
septor.emit('event.happened', ...)

// ✅ CORRECT
septor.emit('payment.refunded', {
  entityId: orgSlug,
  data: { amount, reason, refundId },
  metadata: { who: userId, why: 'Customer requested refund' },
}).catch((err) => logger.error({ err }, 'Septor emit failed — audit event lost'))
```

Install

Copy the skill content and save it to:

~/.claude/rules/tawa-septor-audit.md
Download .md

Coming soon via CLI:

tawa chaac install tawa-septor-audit

Details

Format
Rule
Category
configure
Version
1.0.43050
Tokens
~1,418
Updated
2026-08-28
platformauditcomplianceseptorhash-chainimmutable