Documentation
Home Automation Download

Rut Documentation

Shared projects, local runtime, signed automation.

Rut now centers around a shared-project model. Specs and tickets live in durable project state, sessions execute locally on the active macOS host, collaborators coordinate through shared runtime state, and external systems connect through signed inbound and outbound webhooks.

Important: Rut does not expose a public CLI surface. Public HTTP is work-shaped, not prompt-shaped. Inbound automation can create durable tickets and emit narrow host-bound controls, but it cannot inject arbitrary prompts, override system instructions, or mirror the private MCP relay.

Core Model

The redesign is easiest to reason about if you keep these concepts separate.

Shared Project

The durable collaboration boundary. Specs, tickets, sessions, receipts, subscriptions, and journals hang off this scope.

Spec

A project-shaping artifact. Specs define intent, validation, and the context that tickets can link back to.

Ticket

The canonical unit of durable work. Tickets move through workflow states, claims, approvals, validation, and completion.

Session

A local runtime session associated with the shared project. Sessions read and mutate project state through canonical tools.

Inbound Endpoint

A project-scoped signed webhook entrypoint for ticket.create and optional narrow service controls.

Outbound Subscription

A project-scoped signed delivery target for durable ticket.* and agent.run.* events.

Boundary Split

Rut deliberately uses different surfaces for different trust boundaries.

Surface Audience What It Owns
Shared Project Store Hosts and collaborators Canonical specs, tickets, approvals, receipts, subscriptions, presence, and durable run lifecycle milestones.
Local Runtime Active host device Owning execution, hosting sessions, local process state, and bridging results back into shared project records.
MCP Workflow Tools Local agent sessions only Project reads and writes through canonical tools such as noded_ticket_list, noded_ticket_transition, and noded_spec_get.
Inbound Webhooks External systems Durable work ingress via ticket.create plus narrow feature-gated service controls.
Outbound Webhooks External systems Durable notifications emitted from the backend outbox using signed ticket.* and agent.run.* envelopes.
Control Intents Hosts and collaborators Host-bound actions such as handoff or interrupt. External callers only request these through narrow service controls.
Relay / Live Observation Collaborators Live collaboration fanout. It is not the public automation subscription interface.

Local Runtime & MCP

The app is now the runtime host. Shared project state stays durable and collaborative, while execution stays on the active macOS host that owns the session.

Canonical workflow tools: prefer the noded_ticket_* and noded_spec_* surfaces for local session work. These tools express durable project mutations through the app-owned runtime instead of a public CLI contract.
noded_ticket_list noded_ticket_get noded_ticket_transition noded_ticket_progress_append noded_spec_get noded_spec_patch
  • Runs are owned by the active host device for the shared session.
  • Collaborators observe live progress and coordinate handoffs through shared runtime state.
  • Public webhooks cannot mutate project state arbitrarily; they must pass through narrow, documented envelopes.
  • Host-targeted controls resolve flow, lease, and host ownership on the backend before they are delivered.

Access Automation From Settings

The webhook manager lives directly inside the macOS app and is scoped to the shared projects you currently have open.

1

Sign in

Use an account that can open shared projects from Settings > Account.

2

Open the shared project

Automation is only available for shared projects that are open in the current window.

3

Open Automation settings

Go to Settings > Automation, choose the project, then manage endpoints and subscriptions.

4

Copy secrets once

Endpoint and subscription signing secrets are shown when created or rotated. Store them in the receiving system immediately.

Area Available Actions
Inbound Endpoints Create, edit, revoke, rotate secrets, constrain commands, constrain initial states, and inspect ingress receipts.
Outbound Subscriptions Create, edit, pause, resume, revoke, filter event families, and inspect recent delivery attempts.
Quickstart Copy sample ticket.create and service-control curl payloads directly from the selected endpoint.

Quickstart

This is the fastest path from an external system to a durable Rut ticket lifecycle.

1

Create an inbound endpoint

Start with ticket.create only. Enable service controls later if you explicitly need them.

2

Set allowed initial states

Keep ingress narrow by constraining create states to automation-safe workflow states.

3

Send a signed request

Sign the raw body with your endpoint secret and include the timestamp and signature headers.

4

Create an outbound subscription

Subscribe to the event families you need and verify outbound signatures with the subscription secret.

ticket.create
curl -X POST 'https://noded-35e17.web.app/api/automation/inbound/<endpoint-id>' \
  -H 'Content-Type: application/json' \
  -H 'X-Noded-Timestamp: <unix-seconds>' \
  -H 'X-Noded-Signature: <hmac-sha256>' \
  -d '{
    "version": 1,
    "command": "ticket.create",
    "idempotencyKey": "crm-case-10491",
    "payload": {
      "title": "Investigate failed billing webhook",
      "description": "Raised by Stripe alerting automation.",
      "initialStateId": "intake"
    }
  }'

Inbound ticket.create

Use ticket.create when an external system needs to create durable work inside a shared project.

Field Required Notes
version Yes Current protocol version is 1.
command Yes Must be ticket.create.
idempotencyKey or sourceEventId Yes Rut deduplicates by endpoint plus this key. Replays with the same body return the original result.
payload.title Yes Ticket title.
payload.description No Stored as the ticket description.
payload.initialStateId No Optional create state, validated against endpoint policy and workflow safety rules.
payload.linkedSpecId No Links the new ticket to an existing spec when present.
payload.parentTicketId No Creates a child ticket under an existing parent ticket.
Not allowed: inbound automation cannot set prompts, model overrides, lease identity, host identity, pre-claims, or direct session-turn commands.

Allowed initial states

State Allowed Behavior
intake Yes Default safe ingress state.
backlog Yes, if endpoint policy allows it Useful when upstream triage already happened.
ready Yes, if endpoint policy allows it Useful for already-approved automation-ready work.
review, done, cancelled No Rejected as unsafe create targets.
Success
{
  "ok": true,
  "ticketId": "ticket_01HZY1M8VQ9W",
  "revision": 1,
  "ingressReceiptId": "endpoint_01HZ..._64f3434f..."
}

Narrow Service Controls

Rut supports a very small service-originated live-control surface. These commands are feature-gated and disabled by default.

Command Payload What Rut Does
control.interrupt_turn { "targetSessionId": "..." } Creates a host-targeted control intent that asks the active host to interrupt the current turn.
control.request_handoff { "targetSessionId": "..." } Creates a host-targeted control intent requesting a handoff for the active shared session.
Guardrails: callers provide only targetSessionId. The backend resolves the canonical flow, current lease epoch, host user, host device, and expiry. If there is no active shared host or lease, the command fails.
control.interrupt_turn
curl -X POST 'https://noded-35e17.web.app/api/automation/inbound/<endpoint-id>' \
  -H 'Content-Type: application/json' \
  -H 'X-Noded-Timestamp: <unix-seconds>' \
  -H 'X-Noded-Signature: <hmac-sha256>' \
  -d '{
    "version": 1,
    "command": "control.interrupt_turn",
    "idempotencyKey": "interrupt-481",
    "payload": {
      "targetSessionId": "7C0A7EE6-4E3C-4E19-B9B8-2B0CC804F761"
    }
  }'

Outbound Subscriptions

Outbound automation is delivered from a durable backend outbox fed by canonical ticket journals and durable run lifecycle milestones.

Family Meaning
ticket.created Canonical ticket creation.
ticket.state.changed Workflow movement such as ready → in_progress or review → done.
ticket.claim.changed Ticket claim acquired or released.
ticket.progress.appended Progress journal append.
ticket.validation.changed Validation report or snapshot change.
ticket.approval.requested, ticket.approval.resolved Approval lifecycle changes.
agent.run.started, agent.run.awaiting_approval, agent.run.awaiting_input, agent.run.capacity_paused, agent.run.turn.completed, agent.run.failed, agent.run.done Durable run lifecycle milestones for shared-project execution.

Delivery headers

Header Meaning
X-Noded-Event-ID Stable event id. Deduplicate on this value.
X-Noded-Delivery-ID Unique id for a specific attempt.
X-Noded-Timestamp Unix seconds used in signature verification.
X-Noded-Signature HMAC-SHA256 over timestamp + "." + rawBody.

Delivery is at least once. Rut retries with exponential backoff and records each attempt in the Automation settings pane.

Signing, Replay, and Idempotency

Inbound verification

Compute the request signature over the raw request body and the timestamp header.

Node.js example
import { createHmac } from "crypto";

function signRutWebhook(secret, timestamp, rawBody) {
  return createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`, "utf8")
    .digest("hex");
}
  • Rut rejects missing or invalid signatures.
  • Rut rejects invalid timestamps and requests more than five minutes away from backend clock time.
  • Use sourceEventId when your upstream system already has a stable event identifier.
  • Otherwise use idempotencyKey.
  • Replays with the same dedupe key and the same body return the original result.
  • Replays with the same dedupe key but a different body are rejected as a conflict.

Outbound verification

Outbound deliveries use the same HMAC format: timestamp + "." + rawBody. Verify the signature with the subscription secret, then deduplicate by X-Noded-Event-ID.

Event Envelope Examples

ticket.created
{
  "id": "ticket-event:event-1",
  "type": "ticket.created",
  "version": 1,
  "occurredAt": "2026-03-25T00:00:00.000Z",
  "projectRef": {
    "workspaceId": "workspace-1",
    "projectId": "project-1"
  },
  "entityRef": {
    "ticketId": "ticket-1"
  },
  "actor": {
    "type": "system",
    "displayName": "Automation Webhook",
    "sessionId": "session-1"
  },
  "causation": {
    "ticketEventId": "event-1",
    "ingressReceiptId": "receipt-1",
    "sourceEventId": "source-1",
    "rootRunId": "root-run-1",
    "controlIntentId": null
  },
  "data": {
    "ticketEventKind": "created",
    "summary": "Created remotely",
    "details": "Ticket created via canonical journal",
    "ticketRevision": 3,
    "artifactCount": 1
  }
}
agent.run.awaiting_input
{
  "id": "agent-run:run-event-1",
  "type": "agent.run.awaiting_input",
  "version": 1,
  "occurredAt": "2026-03-25T12:00:00.000Z",
  "projectRef": {
    "workspaceId": "workspace-1",
    "projectId": "project-1"
  },
  "entityRef": {
    "ticketId": "ticket-1",
    "sessionId": "session-1",
    "flowId": "flow-1",
    "rootRunId": "root-run-1",
    "turnId": "turn-1"
  },
  "actor": {
    "type": "session",
    "displayName": "Shared Runtime",
    "sessionId": "session-1"
  },
  "causation": {
    "ticketEventId": null,
    "ingressReceiptId": null,
    "sourceEventId": null,
    "rootRunId": "root-run-1",
    "controlIntentId": "intent-1"
  },
  "data": {
    "detailMessage": "Waiting for approval answer",
    "errorMessage": null,
    "resumeAt": null,
    "hostUserId": "host-user",
    "hostDeviceId": "host-device"
  }
}

Operations

The app exposes recent operational state directly in Automation settings so you can debug integrations from the source of truth.

Ingress receipts

Each inbound request records an endpoint-scoped receipt with command, dedupe key, created ticket or control intent, and status.

Delivery attempts

Each outbound subscription delivery attempt records success or failure, the event id, and the latest error message.

Retries

Outbound deliveries are at least once and retry with exponential backoff. Deduplicate on X-Noded-Event-ID.

Secret rotation

Endpoint and subscription secrets can be rotated in the app. Treat the new secret as authoritative immediately after rotation.

Troubleshooting

Problem Likely Cause What To Check
401 on inbound webhook Bad signature or timestamp skew Verify the raw-body HMAC, the X-Noded-Timestamp value, and that your clock is reasonably accurate.
Duplicate request rejected Same dedupe key, different body Keep the body stable when retrying an event, or issue a new dedupe key for a distinct event.
initialStateId rejected State is outside the endpoint policy or not automation-safe Restrict create states to intake, backlog, or ready.
Service control rejected No active host or lease, or feature flag disabled Confirm the target session belongs to an active shared flow and that service controls are enabled for the deployment.
No outbound deliveries Subscription paused, revoked, or filtered out Review the subscription status and event-family filter in Settings > Automation.
Need operational visibility Looking only at the receiver side Use the in-app Automation pane to inspect ingress receipts, delivery attempts, and the latest failure message.