AI agent skill · by @dust-tt
Dust Audit Log Event
Add a new audit log event in `front`. Use when instrumenting a new user or system action for WorkOS audit logging, including schema creation, `AuditAction` updates, and the correct emit call after the mutation path.
Archived 2026-10-23 · Source updated 2026-10-11 · License: MIT. Check the original repository for the latest version.
SKILL.md instructions
# Front Audit Log Events
Add audit events in `front` by creating the schema, registering the action string, and emitting the
event from the correct success or failure path. The goal is to produce a valid WorkOS event without
blocking the request path.
## Workflow
### 1. Identify the event
Before editing code, determine:
- the action string, usually `<resource>.<verb>`
- the mutation or failure path that should emit it
- whether the actor is a request user, a known user outside an HTTP request, or the system
- the event tier from the Notion audit events database
### 2. Create the WorkOS schema
Create `front/admin/audit_log_schemas/<action>.json`.
Rules:
- `targets` should start with `{ "type": "workspace" }` unless the event truly has no workspace
context
- additional targets describe the affected entities, such as `user`, `space`, `trigger`, `tool`,
`agent`, or `api_key`
- `metadata` is optional, but every metadata value must be declared as `"string"`
- use snake_case metadata keys (see `[audit-snake-case-metadata-keys]`)
Example shape:
```json
{
"action": "resource.verb",
"targets": [
{ "type": "workspace" },
{ "type": "resource" }
],
"metadata": {
"field_name": "string"
}
}
```
### 3. Register the action string
Update `front/lib/api/audit/workos_audit.ts`:
- add the new string to the `AuditAction` union
- place it in the right category section
- keep the entries alphabetized inside that section
### 4. Emit the event from the right path
Emit after the mutation succeeds unless the event explicitly represents failure.
For request-driven actions with an `Authenticator`, use:
- `emitAuditLogEvent`
- `buildWorkspaceTarget`
- `getAuditLogContext`
Pattern:
```typescript
void emitAuditLogEvent({
auth,
action: "resource.verb",
targets: [
buildWorkspaceTarget(auth.getNonNullableWorkspace()),
{ type: "resource", id: resource.sId, name: resource.name },
],
context: getAuditLogContext(auth),
metadata: {
field_name: String(value),
},
});
```
For system-driven actions such as Temporal or inbound webhooks, use `emitAuditLogEventDirect`
with an explicit system actor:
```typescript
void emitAuditLogEventDirect({
workspace,
action: "resource.verb",
actor: { type: "system", id: "temporal", name: "Directory Sync" },
targets: [buildWorkspaceTarget(workspace)],
context: { location: "internal" },
});
```
For non-HTTP code paths where you know the user but do not already have an `Authenticator`, create
one with `Authenticator.fromUserIdAndWorkspaceId(...)` and then use `emitAuditLogEvent(...)`.
### 5. Keep the emit call non-blocking
Use `void`, not `await`, for audit emission. Audit logging should not block the main write path.
### 6. Register the schema and commit the version map (pre-merge)
WorkOS drops events whose schemas were never registered, and validates each emitted event against
the schema *version* we send. Register from your branch **before merging**, then commit the
generated version map in the same PR — the same way a DB migration file ships with the code that
needs it.
```bash
# Preview (dry-run) — does not touch WorkOS or the version map.
npx tsx front/admin/register_audit_log_schemas.ts --changed
# Register the new/changed schemas and write the version map.
npx tsx front/admin/register_audit_log_schemas.ts --execute --changed
# Or register specific actions.
npx tsx front/admin/register_audit_log_schemas.ts --execute user.login api_key.created
```
The `--execute` runs register the schemas with WorkOS and merge the latest version for each
registered action into `front/lib/api/audit/schema_versions.json`. **Commit
`schema_versions.json` in the same PR.** `createAuditLogEvent` reads it to send the right version
with every event; a stale or missing version causes WorkOS validation failures.
A CI test (`audit_log_schemas.test.ts`) fails if any `AuditAction` has no entry in the version map,
so a forgotten registration is caught before merge — just like a missing migration.
DangerJS also fails the PR if a schema file under `front/admin/audit_log_schemas/` changed but its
version in `schema_versions.json` was not bumped relative to `main` (i.e. the registration step was
skipped). In the rare case the edit is a no-op WorkOS treats as identical (so no new version is
created), add the `audit-log-ack` label to override.
## Validation
Check all of the following:
- the schema file exists at `front/admin/audit_log_schemas/<action>.json`
- the action string was added to `AuditAction`
- the emit call runs after the intended success path, or on the intended failure path for failure
events
- `targets` start with the workspace target when a workspace exists
- metadata values are wrapped with `String(...)` when needed
- request-backed events use `getAuditLogContext(auth)` (the client IP comes from the `Authenticator`, set by the auth middleware)
- the emit call uses `void`
- the targets in the emit call exactly match (in type and count) the targets in the schema JSON file
- if modifying an existing event's targets, the schema JSON file has been updated to match
- if a schema JSON file was added or changed, `register_audit_log_schemas.ts --execute --changed` must be run pre-merge and the regenerated `front/lib/api/audit/schema_versions.json` committed in the same PR (the `audit_log_schemas.test.ts` version check enforces this)
## References
- `front/lib/api/audit/workos_audit.ts`
- `front/admin/audit_log_schemas/`
- `front/CONTRACTS` entries with the `audit-` ID prefix