Liners: Governance as Code

Composable control layers that wrap event handlers to enforce safety, observability, and governance without touching agent code

Overview

In Happen, we believe that control should be composable. Most frameworks force developers into a false dichotomy between:

  • Safety: Rigid, restrictive platforms that limit what agents can do

  • Power: Raw, dangerous runtime access with no guardrails

  • Happen transcends this trade-off through a pattern we call Liners—giving you both safety and power simultaneously.

What is a Liner?

A Liner is a thin, protective layer that wraps an event handler. It allows you to:

  1. Inspect events before they reach your logic

  2. Transform or modify data flowing through the system

  3. Block dangerous operations before execution

  4. Observe results after completion

At its core, a Liner is simply a Higher-Order Function. It takes a next function (the original handler) and returns a new, enhanced handler function.

Anatomy of a Liner

// The Anatomy of a Liner
const myLiner = (next) => {
  return (event, context) => {
    // 1. Pre-Process: Inspect the event before execution
    console.log(`Processing ${event.type}...`);

    // 2. Execute: Call the original handler
    const result = next(event, context);

    // 3. Post-Process: Inspect the result
    console.log(`Finished with status: ${result.status}`);

    return result;
  };
};                                                                                          

Composing Liners

The true power of Liners emerges when you stack them. You can layer multiple behaviors—logging, budgeting, safety checks—onto a single node, creating a composable security architecture.

We provide a utility called line() to compose Liners elegantly.

import { createNode } from "happen";
import { line } from "./utils";

const fsNode = createNode("file-system");

// The raw, dangerous capability
const deleteHandler = (event) => {
  Bun.file(event.payload.path).delete();
  return { deleted: true };
};

// The protected, observable handler
fsNode.on(
  "file.delete",
  line(
    deleteHandler, // The core logic
    withLogging, // Liner 1: Log the attempt
    withShadowMode, // Liner 2: Intercept writes (Safety)
  ),
);

In this stack, the event flows through withShadowMode → withLogging → deleteHandler. If withShadowMode determines the action is unsafe, it returns early, and deleteHandler never executes.

Order Matters: Liners execute in reverse order of declaration. The last Liner listed runs first, wrapping each previous layer.

Practical Examples

Let's explore real-world Liners that solve critical problems in agentic systems:

Shadow Mode Liner (Safety)

Transform dangerous tools into safe ones by intercepting destructive actions and virtualizing them for human review.

export const withShadowMode = (next) => (event, context) => {
  const isDestructive = event.type.includes("delete") ||
    event.type.includes("overwrite");

  if (isDestructive && !event.context.approved) {
    // INTERCEPT: Don't call next(). Stage for review instead.
    ShadowSystem.stageChange({
      type: event.type,
      payload: event.payload,
      risk: "high",
    });

    return {
      status: "paused",
      message: "Operation halted for human review.",
    };
  }
};

Last updated