Skip to content

Architecture

Metis is a TypeScript monorepo around one idea: the workflow definition is data, Temporal makes it durable, and every substrate is behind a port.

 Browser (metis-editor)
    | HTTP + WebSocket
 metis-core (control plane: auth, CRUD, catalogue, connections)
    |                         \
 metis-orchestrator            metis-data-gateway (SQLite/Postgres)
 (Temporal client, schedules,
  socket hub, triggers)
    |
 Temporal server
    |
 metis-engine worker (helixWorkflow + activities)
    |
 metis-nodes handlers (HTTP, code sandbox, data sources, connectors)

The packages

Package What it is
metis-ports The seams: NodeExecPort, CredentialPort, DataSourcePort, EventSink, IdentityPort, ExecutionPort + in-memory fakes. Everything depends on ports; ports depend on nothing.
metis-catalogue Node types and connectors as data: nodeTypes.v1.json (config/output schemas, palette, docs) + the connector registry. The editor renders from it; the engine validates against it.
metis-engine The Temporal worker. helixWorkflow walks the definition as a one-shot DAG (waves, fan-in joins, orphan cascades); inline control nodes (switch/logic/loop/filter/merge/...) run in the dispatch activity; the Loop spawns real child workflows. Activities are the only substrate access.
metis-nodes Open node handlers: HTTP/api, sandboxed code, the Data node's Postgres adapter, SendGrid, generated connector handlers.
metis-core The control-plane HTTP surface: auth + sessions, workflow CRUD, executions, connections (write-only credentials), the data-resource routes, catalogue serving.
metis-orchestrator The Temporal client side: execution adapter (start/signal/cancel/describe/list), schedule service, the Socket.IO hub streaming engine events, trigger services.
metis-data-gateway Storage behind one gateway: SQLite for the laptop, Postgres for real deployments. Workflow store, execution logs, connections.
metis-cli npx @mindlynx/metis-cli - init/up/dev; downloads and manages a local Temporal dev server; collapses core+orchestrator+worker into one process for the laptop.
metis-editor The React app: React Flow canvas, schema-driven inspectors, runs pages.

Key invariants

  • Generic I/O: a node receives the run's state and gives one payload; only its config is typed. Downstream steps reference outputs as {{node-<id>.data.<path>}}, substituted in the dispatch activity.
  • Determinism: workflow code never touches substrate; anything non-deterministic happens in activities and rides Temporal history.
  • Branching: branch nodes return selected/orphaned target sets; the walker orphans losing branches (skipped, not failed) with convergence protection.
  • Payload discipline: node outputs are capped (the data node ~256 KB) so state survives Temporal's payload limits; big data travels as references.
  • Retention: Metis's store keeps execution history for retentionDays (metis.config.json, default 90) - far beyond the Temporal dev server's visibility window. Operate's Archive lists runs Temporal has forgotten; their detail pages stay fully inspectable from the store.
  • Editions: the open build is complete and self-contained; gated capabilities exist only as locked cards. Six release gates enforce the boundary structurally.

Where things happen

  • Engine walk: packages/metis-engine/src/workflows/helixWorkflow.ts
  • Node dispatch + substitution: packages/metis-engine/src/activities/create-activities.ts
  • Definition validation (incl. cycle + loop rules): packages/metis-engine/src/validation.ts
  • Control-plane routes: packages/metis-core/src/*.ts
  • Live events: engine activities emit -> LocalEventBus -> socket hub (packages/metis-orchestrator/src/socket-hub.ts) -> the editor.