Skip to content

Auckfmine/relay

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Relay — Workflow Automation Platform

Relay is a multi-tenant workflow automation platform inspired by Make.com and n8n.

Getting started in 5 minutes

  1. Copy env defaults
cp .env.example .env
  1. Start the stack
make up
  1. Run database migrations
make migrate
  1. Open the services
  1. Run tests
make test

Identity + RBAC quickstart

  1. Register a user (dev only)
curl -X POST http://localhost:8000/api/v1/auth/register \
  -H 'Content-Type: application/json' \
  -d '{"email":"owner@example.com","password":"password123","name":"Owner"}'
  1. Login
curl -X POST http://localhost:8000/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"owner@example.com","password":"password123"}'
  1. Create a workspace
curl -X POST http://localhost:8000/api/v1/workspaces \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Relay Ops"}'
  1. Create an API key (owner-only)
curl -X POST http://localhost:8000/api/v1/workspaces/<workspace_id>/api-keys \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Content-Type: application/json' \
  -d '{"name":"CI Key"}'

Run a workflow

  1. Publish a workflow from the editor UI or via API:
curl -X POST http://localhost:8000/api/v1/workspaces/<workspace_id>/workflows/<workflow_id>/publish \
  -H 'Authorization: Bearer <access_token>'
  1. Trigger a manual run:
curl -X POST http://localhost:8000/api/v1/workspaces/<workspace_id>/workflows/<workflow_id>/run \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Content-Type: application/json' \
  -d '{"trigger_payload":{"hello":"world"}}'
  1. Inspect runs + steps:
  • GET /api/v1/workspaces/<workspace_id>/runs
  • GET /api/v1/workspaces/<workspace_id>/runs/<run_id>/steps

Notes:

  • At-least-once execution; steps may be retried.
  • Cancellation stops future steps but does not terminate in-flight tasks.

Core nodes (v1)

Supported node types:

  • core.http_request
  • core.transform
  • logic.if_else
  • core.delay
  • core.fail
  • core.stop

HTTP Request node safety

The HTTP node blocks private, loopback, link-local, and metadata IPs by default. You can also configure host allow/deny lists via env vars:

  • HTTP_ALLOWLIST_HOSTS (comma-separated)
  • HTTP_DENYLIST_HOSTS (comma-separated)
  • HTTP_DENYLIST_CIDRS (comma-separated)

Sensitive headers (Authorization, Cookie, Set-Cookie, X-API-Key) are redacted in outputs. You can specify extra redacted fields via the node config.

HTTP body modes:

  • JSON
  • Form URL-Encoded
  • Multipart (simple key/value)

Expressions & mapping (v1)

Expressions allow mapping data between steps using {{ ... }} syntax. Examples:

  • {{ trigger.body.customer_id }}
  • {{ steps.http1.output.body.id }}
  • Hello {{ steps.http1.output.body.name }}

Roots:

  • trigger (trigger payload)
  • steps.<nodeKey>.output (prior step outputs)

Functions:

  • coalesce(a,b,...)
  • default(x, fallback)
  • upper(s), lower(s)
  • len(x)
  • now()
  • dateAdd(ts, amount, unit) where unit is days|hours|minutes
  • toJson(x), fromJson(s)
  • concat(a,b,...)
  • substr(s, start, len)

Notes:

  • Expressions are deterministic and do not execute code.
  • Missing paths resolve to null in full expression mode or empty string in templates.
  • The editor uses Monaco for advanced expression editing (autocomplete + inline errors).

Webhook triggers (v1)

  1. Configure draft triggers:
curl -X PUT http://localhost:8000/api/v1/workspaces/<workspace_id>/workflows/<workflow_id>/draft/triggers \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "triggers": [
      {
        "type": "webhook",
        "name": "Inbound",
        "config": {
          "signature_required": true,
          "replay_protection_enabled": true
        }
      }
    ]
  }'
  1. Publish and fetch active triggers:
curl http://localhost:8000/api/v1/workspaces/<workspace_id>/workflows/<workflow_id>/triggers \
  -H 'Authorization: Bearer <access_token>'
  1. Rotate secret (shown once):
curl -X POST http://localhost:8000/api/v1/workspaces/<workspace_id>/workflows/<workflow_id>/triggers/<trigger_id>/webhook/rotate-secret \
  -H 'Authorization: Bearer <access_token>'
  1. Send a signed webhook: Headers:
X-Timestamp: <unix_seconds>
X-Nonce: <unique nonce>
X-Signature: HMAC_SHA256(secret, "<timestamp>.<nonce>.<raw_body>")

Example (bash + openssl):

body='{"hello":"world"}'
timestamp=$(date +%s)
nonce=$(uuidgen)
signature=$(printf "%s.%s.%s" "$timestamp" "$nonce" "$body" | openssl dgst -sha256 -hmac "<secret>" | sed 's/^.* //')
curl -X POST http://localhost:8000/api/v1/hooks/<public_id> \
  -H "Content-Type: application/json" \
  -H "X-Timestamp: $timestamp" \
  -H "X-Nonce: $nonce" \
  -H "X-Signature: $signature" \
  -d "$body"

Schedule triggers (v1)

Configure a cron schedule on the draft triggers:

curl -X PUT http://localhost:8000/api/v1/workspaces/<workspace_id>/workflows/<workflow_id>/draft/triggers \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "triggers": [
      {
        "type": "schedule",
        "name": "Every minute",
        "config": {
          "cron": "* * * * *",
          "timezone": "UTC"
        }
      }
    ]
  }'

The scheduler scans every SCHEDULE_SCAN_INTERVAL_SECONDS and enqueues due runs.

RBAC summary

  • owner: full control (manage members + API keys + workflows)
  • admin: manage members + workflows (cannot assign owner, cannot revoke API keys)
  • member: create and edit workflows
  • viewer: read-only workspace access

Workflows

Workflows are versioned automation graphs with a professional lifecycle:

Concepts

  • Draft: Editable graph stored on the workflow. Autosaves on changes.
  • Published Version: Immutable snapshot created when you publish. Executions reference these.
  • Rollback: Set a previous version as the current published version (history preserved).

Creating a Workflow

  1. Create a workflow
curl -X POST http://localhost:8000/api/v1/workspaces/<workspace_id>/workflows \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Content-Type: application/json' \
  -d '{"name":"My Workflow"}'
  1. Save draft graph
curl -X PUT http://localhost:8000/api/v1/workspaces/<workspace_id>/workflows/<workflow_id>/draft \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "graph": {
      "nodes": [
        {"id": "n1", "key": "webhook", "type": "trigger.webhook", "position": {"x": 100, "y": 100}, "config": {}}
      ],
      "edges": [],
      "metadata": {}
    }
  }'
  1. Validate the draft
curl -X POST http://localhost:8000/api/v1/workspaces/<workspace_id>/workflows/<workflow_id>/validate \
  -H 'Authorization: Bearer <access_token>'
  1. Publish (creates immutable version)
curl -X POST http://localhost:8000/api/v1/workspaces/<workspace_id>/workflows/<workflow_id>/publish \
  -H 'Authorization: Bearer <access_token>'
  1. View version history
curl http://localhost:8000/api/v1/workspaces/<workspace_id>/workflows/<workflow_id>/versions \
  -H 'Authorization: Bearer <access_token>'
  1. Rollback to a previous version
curl -X POST http://localhost:8000/api/v1/workspaces/<workspace_id>/workflows/<workflow_id>/rollback \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Content-Type: application/json' \
  -d '{"version_id": "<version_id>"}'

Validation Rules (v1)

  • Exactly 1 trigger node (type starts with trigger.)
  • No cycles (DAG only)
  • All edges connect existing nodes
  • Node keys must be unique
  • Required config fields per node type
  • Warns if nodes are unreachable from trigger

Graph Format (v1)

{
  "nodes": [
    {
      "id": "unique-id",
      "key": "human-readable-key",
      "type": "trigger.webhook|action.http|...",
      "position": {"x": 100, "y": 100},
      "config": {},
      "retry": {"max_attempts": 3, "initial_delay_ms": 1000},
      "timeout_ms": 30000
    }
  ],
  "edges": [
    {"id": "edge-id", "source": "node-id", "target": "node-id"}
  ],
  "metadata": {"viewport": {"x": 0, "y": 0, "zoom": 1}}
}

Workflow API Endpoints

Method Endpoint Description
POST /workspaces/{id}/workflows Create workflow
GET /workspaces/{id}/workflows List workflows
GET /workspaces/{id}/workflows/{wf_id} Get workflow
PATCH /workspaces/{id}/workflows/{wf_id} Update metadata
DELETE /workspaces/{id}/workflows/{wf_id} Soft delete
GET /workspaces/{id}/workflows/{wf_id}/draft Get draft
PUT /workspaces/{id}/workflows/{wf_id}/draft Save draft
POST /workspaces/{id}/workflows/{wf_id}/validate Validate draft
POST /workspaces/{id}/workflows/{wf_id}/publish Publish version
GET /workspaces/{id}/workflows/{wf_id}/versions List versions
GET /workspaces/{id}/workflows/{wf_id}/versions/{v_id} Get version
POST /workspaces/{id}/workflows/{wf_id}/rollback Rollback

Repo layout

  • backend/api FastAPI service
  • backend/worker Celery worker
  • backend/scheduler Celery Beat
  • backend/common shared libs (config, logging, DB, schemas)
  • frontend React app (React Flow + TanStack Router/Query + shadcn/ui + Zustand)
  • infra Docker + Compose

Useful commands

  • make up Start all services
  • make down Stop all services
  • make logs Tail service logs
  • make migrate Apply Alembic migrations
  • make test Run backend + frontend tests

Environment variables

See .env.example for defaults used by Docker Compose.

CI-ready commands

  • Backend: pytest
  • Migrations: alembic -c backend/common/alembic.ini upgrade head
  • Frontend: npm run typecheck && npm run build

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published