Skip to content

Pack Architecture

Taskless Packs are monitoring components that observe HTTP requests and responses between your application and external services. They capture telemetry data without requiring changes to your existing code.

Packs monitor HTTP traffic by intercepting requests and responses. When your application communicates with third-party services, packs analyze this traffic and extract useful data. Your application continues to function normally while packs collect information in the background.

Here’s how packs integrate with your application:

┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Your │───►│ Taskless │───►│ 3rd Party │
│ Application │◄───│ Loader │◄───│ Service │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ (copies of req/resp)
┌─────────────────┐
│ Pack Collection │
│ (WebAssembly │
│ Plugins) │
└─────────────────┘
│ (pack output)
┌─────────────────┐
│ Logs + │
│ Telemetry │
└─────────────────┘
  1. Your application initiates a request to a third-party service
  2. The Taskless Loader intercepts the request and forwards it to the service
  3. Packs receive copies of the request and response data for processing
  4. Your application receives the response without any modification
  5. Telemetry data is sent to your configured destination

Packs run in WebAssembly, a secure runtime environment that provides several important capabilities:

Security: WebAssembly operates in a sandboxed environment, preventing packs from affecting your application or system Performance: WebAssembly executes at near-native speed with minimal performance overhead Language Support: You can write packs in any language that compiles to WebAssembly Isolation: Packs cannot access external resources beyond the HTTP data they’re designed to monitor

Packs operate under strict limitations to ensure system security:

  • Network isolation - Cannot make HTTP requests or network connections
  • File system isolation - Cannot read or write files
  • System isolation - Cannot execute system calls or access operating system functions
  • Memory limits - Operate within controlled memory boundaries
  • Read-only access - Can observe HTTP data but cannot modify requests or responses

Packs implement up to three methods that execute at different stages of an HTTP request:

Executes before the HTTP request is sent to the third-party service.

Common uses:

  • Capture request metadata (URL, headers, method)
  • Access configuration values
  • Initialize tracking variables
  • Set up context for subsequent methods

Available data:

  • Outgoing request details (URL, headers, method, body when enabled)
  • Pack configuration settings
  • Environment context

Executes for each chunk of response data as it arrives. This method requires explicit enablement in the pack manifest.

Common uses:

  • Process streaming responses (Server-Sent Events, chunked transfer encoding)
  • Extract data from partial responses
  • Monitor large download progress
  • Handle real-time data streams

Available data:

  • Individual data chunks (base64-encoded)
  • Context from previous lifecycle methods
  • State accumulated from earlier chunks

Executes after the complete HTTP response is received.

Common uses:

  • Analyze complete response data
  • Calculate metrics (response time, data size)
  • Extract business-relevant information
  • Finalize tracking and cleanup

Available data:

  • Complete response (status code, headers, body)
  • Original request data
  • Context from pre and chunk methods

When you install a pack, Taskless creates a .taskless directory containing organized pack files:

.taskless/
└── [pack-name]/
├── manifest.json # Pack metadata and configuration schema
├── pack.wasm # Compiled WebAssembly binary
└── config.json # User configuration values

The manifest defines the pack’s metadata and configuration interface. Here’s an example from the Taskless pack-example repository:

{
"schema": "pre2",
"name": "testpack",
"version": "0.0.5",
"description": "Test / example pack, never published to prod",
"permissions": {
"body": true
},
"fields": [
{
"name": "testField",
"type": "string",
"description": "Test field that should exist in the lifecycle",
"default": "test_field_value"
}
],
"charts": []
}

This manifest declares the pack’s name, version, required permissions, and available configuration fields.

The pack.wasm file contains the compiled pack code that:

  • Implements the lifecycle methods (pre, post, chunk)
  • Executes within the WebAssembly security sandbox
  • Communicates through the Extism plugin interface

The config.json file contains user-specific settings:

{
"testField": "custom_value"
}

This overrides the default value specified in the manifest.

Packs support configurable behavior through a field-based system that allows customization without code changes.

  • string: Text values such as API keys, URLs, or descriptive text
  • string[]: Arrays of strings for domain lists, tags, or multiple values
  • number: Numeric values for thresholds, timeouts, or quantities
  • number[]: Arrays of numbers for ranges, percentiles, or multiple numeric values
  • boolean: True/false flags for enabling or disabling features

Pack configuration values are resolved using the following priority:

  1. User configuration - Values specified in config.json
  2. Manifest defaults - Default values defined in the pack manifest
  3. Undefined - No value if neither source provides one

This hierarchy allows pack authors to provide sensible defaults while giving users full control over pack behavior.

Packs can be written in any programming language that compiles to WebAssembly. The Extism PDK ecosystem provides development kits for multiple languages including JavaScript/TypeScript, Rust, Go, Python, C/C++, Zig, Haskell, and F#.

All languages produce compatible WebAssembly modules that use the same lifecycle interface, ensuring consistent behavior regardless of implementation language.

Packs can maintain state between lifecycle methods using a context system. This allows data collected in one method to be used in subsequent methods:

// In pre(): Set up tracking
writeOutput({
context: {
startTime: Date.now(),
requestId: generateId(),
},
capture: {
url: request.url,
},
});
// In post(): Use context from pre()
const duration = Date.now() - input.context.startTime;
writeOutput({
capture: {
requestId: input.context.requestId,
duration: duration,
},
});

Packs generate telemetry data through the capture field in their output. This data becomes part of your monitoring and observability pipeline:

  • Structured data for querying and analysis
  • Time-series metrics for dashboards and alerting
  • Event tracking for business intelligence
  • Error detection for reliability monitoring

The pack architecture provides a secure foundation for observing application interactions with external services while maintaining isolation and zero-trust principles.