Skip to content
Back to skills

076 Api D8349bfa

ASecurity

```typescript export default { async tail( events: TraceItem[], env: Env, ctx: ExecutionContext ): Promise<void> { // Process events } } satisfies ExportedHandler<Env>; ```

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
datatypescriptgitapi

Works with

  • api

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 076-api_d8349bfa --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 076 Api D8349bfa?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 076 Api D8349bfa
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-076-api-d8349bfa/badge)](https://www.skillsdirectory.com/skills/tools-only-076-api-d8349bfa)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Tail Workers API Reference

## Handler Signature

```typescript
export default {
  async tail(
    events: TraceItem[],
    env: Env,
    ctx: ExecutionContext
  ): Promise<void> {
    // Process events
  }
} satisfies ExportedHandler<Env>;
```

**Parameters:**
- `events`: Array of `TraceItem` objects (one per producer invocation)
- `env`: Bindings (KV, D1, R2, env vars, etc.)
- `ctx`: Context with `waitUntil()` for async work

**CRITICAL:** Tail handlers don't return values. Use `ctx.waitUntil()` for async operations.

## TraceItem Type

```typescript
interface TraceItem {
  scriptName: string;           // Producer Worker name
  eventTimestamp: number;        // Epoch milliseconds
  outcome: 'ok' | 'exception' | 'exceededCpu' | 'exceededMemory' 
         | 'canceled' | 'scriptNotFound' | 'responseStreamDisconnected' | 'unknown';
  
  event?: {
    request?: {
      url: string;               // Redacted by default
      method: string;
      headers: Record<string, string>;  // Sensitive headers redacted
      cf?: IncomingRequestCfProperties;
      getUnredacted(): TraceRequest;    // Bypass redaction (use carefully)
    };
    response?: {
      status: number;
    };
  };
  
  logs: Array<{
    timestamp: number;           // Epoch milliseconds
    level: 'debug' | 'info' | 'log' | 'warn' | 'error';
    message: unknown[];          // Args passed to console function
  }>;
  
  exceptions: Array<{
    timestamp: number;           // Epoch milliseconds
    name: string;                // Error type (Error, TypeError, etc.)
    message: string;             // Error description
  }>;
  
  diagnosticsChannelEvents: Array<{
    channel: string;
    message: unknown;
    timestamp: number;           // Epoch milliseconds
  }>;
}
```

**Note:** Official SDK uses `TraceItem`, not `TailItem`. Use `@cloudflare/workers-types` for accurate types.

## Timestamp Handling

All timestamps are **epoch milliseconds**, not seconds:

```typescript
// ✅ CORRECT - use directly with Date
const date = new Date(event.eventTimestamp);

// ❌ WRONG - don't multiply by 1000
const date = new Date(event.eventTimestamp * 1000);
```

## Automatic Redaction

By default, sensitive data is redacted from `TraceRequest`:

### Header Redaction

Headers containing these substrings (case-insensitive):
- `auth`, `key`, `secret`, `token`, `jwt`
- `cookie`, `set-cookie`

Redacted values show as `"REDACTED"`.

### URL Redaction

- **Hex IDs:** 32+ hex digits → `"REDACTED"`
- **Base-64 IDs:** 21+ chars with 2+ upper, 2+ lower, 2+ digits → `"REDACTED"`

## Bypassing Redaction

```typescript
export default {
  async tail(events, env, ctx) {
    for (const event of events) {
      // ⚠️ Use with extreme caution
      const unredacted = event.event?.request?.getUnredacted();
      // unredacted.url and unredacted.headers contain raw values
    }
  }
};
```

**Best practices:**
- Only call `getUnredacted()` when absolutely necessary
- Never log unredacted sensitive data
- Implement additional filtering before external transmission
- Use environment variables for API keys, never hardcode

## Type-Safe Handler

```typescript
interface Env {
  LOGS_KV: KVNamespace;
  ANALYTICS: AnalyticsEngineDataset;
  LOG_ENDPOINT: string;
  API_TOKEN: string;
}

export default {
  async tail(
    events: TraceItem[],
    env: Env,
    ctx: ExecutionContext
  ): Promise<void> {
    const payload = events.map(event => ({
      script: event.scriptName,
      timestamp: event.eventTimestamp,
      outcome: event.outcome,
      url: event.event?.request?.url,
      status: event.event?.response?.status,
    }));
    
    ctx.waitUntil(
      fetch(env.LOG_ENDPOINT, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload),
      })
    );
  }
} satisfies ExportedHandler<Env>;
```

## Outcome vs HTTP Status

**IMPORTANT:** `outcome` is script execution status, NOT HTTP status.

- Worker returns 500 → `outcome='ok'` if script completed successfully
- Uncaught exception → `outcome='exception'` regardless of HTTP status
- CPU limit exceeded → `outcome='exceededCpu'`

```typescript
// ✅ Check outcome for script execution status
if (event.outcome === 'exception') {
  // Script threw uncaught exception
}

// ✅ Check HTTP status separately
if (event.event?.response?.status === 500) {
  // HTTP 500 returned (script may have handled error)
}
```

## Serialization Considerations

`log.message` is `unknown[]` and may contain non-serializable objects:

```typescript
// ❌ May fail with circular references or BigInt
JSON.stringify(events);

// ✅ Safe serialization
const safePayload = events.map(event => ({
  ...event,
  logs: event.logs.map(log => ({
    ...log,
    message: log.message.map(m => {
      try {
        return JSON.parse(JSON.stringify(m));
      } catch {
        return String(m);
      }
    })
  }))
}));
```

**Common serialization issues:**
- Circular references in logged objects
- `BigInt` values (not JSON-serializable)
- Functions or symbols in console.log arguments
- Large objects exceeding body size limits

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.