A Developer Integration Platform built on the principle of Integration-as-Code. We replace legacy, opaque IPaaS "Low-Code" designers with a high-velocity workflow developers actually love.
We provide the magic developer experience of Vercel or Temporal for enterprise C# integrations. We eliminate "Click-Ops" by allowing the Code to be the Manifest.
- Author: Write a C# class and decorate it with
[ScheduledIntegration]. - Develop:
serto devwatches your code and runs tests locally on every save. - Deploy:
serto deploy. The Control Plane auto-provisions the infrastructure based on your code.
We are the "Terraform for Enterprise Integrations," designed specifically for engineering teams who need to solve complex SAP, Salesforce, and internal API automation without proprietary lock-in.
Near-term product priorities:
- Version-pinned package execution
- Work item queue
- Workflow DAG foundation
- Retry policy
- Agent heartbeats and agent pools
- Webhook triggers
- Core connectors for HTTP, SQL, files/SFTP, object storage, and notifications
- Code-first transform steps
- Environment promotion
- Audit and RBAC
- Backend: ASP.NET Core 10, EF Core, PostgreSQL
- Frontend: React 19, Vite, TypeScript, TanStack Query, shadcn/ui, Tailwind
- Installation Guide — local dev, Docker/production, configuration reference
- Quick Start — run the platform and deploy your first integration in ~10 minutes
- Writing integrations — SDK, triggers, connectors, and secrets
- API reference — control-plane HTTP API
- Architecture — how the control plane, agent, and CLI fit together
The sections below are a condensed version of the Installation Guide.
- .NET 10 SDK
- Node.js 20+
- Docker
docker-compose -f docker-compose.dev.yml up -d$HOME/.dotnet/dotnet run --project src/ControlPlaneRuns on http://localhost:5000. Migrations are applied automatically on startup.
cd src/ControlPlane.Client && npm run devRuns on http://localhost:5173. API calls are proxied to the backend automatically.
Note: Use port 5173 during development, not 5000. Hot module replacement means changes are reflected instantly without rebuilding.
Navigate to http://localhost:5173 — you'll be directed to /setup to create the first tenant and admin user.
| Field | Value |
|---|---|
| Host | localhost |
| Port | 5433 |
| Database | integration_platform |
| Username | devuser |
| Password | devpassword |
Port is 5433 (not the default 5432) to avoid conflicts with any local Postgres instance.
Agent tokens allow a runtime agent to fetch decrypted secrets from the control plane. They are scoped to a single environment — a production token cannot access staging secrets.
- Go to Agent tokens in the UI and click New token
- Give it a name and set the environment (e.g.
production) - Copy the token value — it is shown once only and cannot be retrieved again
The agent calls the secret bundle endpoint with the token in the X-Agent-Token header:
GET /api/agent/secrets/{environment}
X-Agent-Token: agt_<token>
Response is a decrypted key/value map:
{
"secrets": {
"DATABASE_URL": "postgres://...",
"API_KEY": "sk-..."
}
}The agent injects these into the job's execution environment before running an integration.
The runtime agent is a separate process that executes integrations. It polls the control plane for work, fetches secrets, and runs your C# integration classes.
Create an appsettings.json in the RuntimeAgent project directory:
{
"Agent": {
"ControlPlaneUrl": "http://localhost:5000",
"AgentToken": "agt_<your-token>",
"Environment": "production",
"IntegrationsPath": "./integrations",
"PackagesPath": "./packages",
"PollIntervalSeconds": 30,
"PackageSyncIntervalSeconds": 300,
"MaxConcurrentExecutions": 5,
"ShutdownDrainSeconds": 30
}
}dotnet run --project src/RuntimeAgentThe agent will:
- Load integration assemblies from
IntegrationsPath - Sync uploaded integration packages into
PackagesPath - Poll the control plane for claimed due integrations matching its environment
- Execute the integrations returned by the control plane
- Report execution results back to the control plane
Scheduling state is persisted in the control plane, so agent restarts do not reset cron evaluation.
The runtime agent loads assemblies from the local filesystem and can sync uploaded packages from the control plane. Synced packages are downloaded, SHA-256 verified, extracted under PackagesPath, and loaded by the agent. Integrations can be pinned to uploaded package versions so future executions use deterministic code.
- Build your integration project:
dotnet publish -c Release - Zip the publish output if you want to store the package in the control plane:
cd bin/Release/net10.0/publish zip -r integrations.zip . curl -X POST http://localhost:5000/api/integration-packages \ -H "Authorization: Bearer <jwt>" \ -F "name=MyCompany.Integrations" \ -F "version=1.0.0" \ -F "file=@integrations.zip"
- The agent will download and extract the package on startup or at the next package sync interval
- Register or update the integration in the control plane with the fully qualified class name and package version
The CLI can scan, package, and upload the current integration project:
dotnet run --project src/Cli -- scan
dotnet run --project src/Cli -- package \
--name MyCompany.Integrations \
--version 1.0.0
SERTO_API_TOKEN=<personal-access-token> dotnet run --project src/Cli -- deploy \
--url http://localhost:5000 \
--name MyCompany.Integrations \
--version 1.0.0serto scan, serto package, and serto deploy all show the same discovery preview: package metadata, decorated integration classes, trigger declarations, run policy, validation errors, and required secret names detected from connector/context usage. If --version is omitted, serto deploy uses PackageVersion or Version from the project file and falls back to a timestamped development version. You can still copy published DLLs directly to the agent's IntegrationsPath for local development. Package sync avoids manual copying; integrations can be pinned to uploaded package versions, and rollback is performed by repointing the integration to a previous package version.
See docs/writing-integrations.md for details.
cd src/ControlPlane.Client && npm run buildOutput goes to src/ControlPlane/wwwroot and is served by the .NET server at http://localhost:5000.
Production deployments use published Docker images, so servers do not need a source checkout:
cp .env.example .env
# Fill in SERTO_CONTROL_PLANE_CONNECTION_STRING, JWT_SECRET, and ENCRYPTION_MASTER_KEY.
docker compose -f docker-compose.prod.yml up -dThe production compose file pulls craytech/serto.controlplane:${SERTO_IMAGE_TAG:-1.0.3} and points it at an external PostgreSQL database. Runtime agents are deployed separately on hosts that can reach the systems your integrations use:
docker compose -f docker-compose.agent.yml up -dFor a single-host trial with PostgreSQL and the control plane together, use docker-compose.trial.yml.
For maintainers publishing images, see Docker Images.
scripts/validate.shThis runs patch hygiene checks, .NET restore/build/test, and frontend lint/build when node_modules is present.
Currently 476 tests cover control plane features, SDK behavior, runtime agent behavior, CLI behavior (including saved-credential storage, project scaffolding, name validation, and local serto test validation), connector configuration validation and test harness, API contracts, RBAC/security boundaries, audit logging, failure alerting, environment management, commercial API surfaces, and database-backed integration paths.
The control plane integration tests use a temporary PostgreSQL database when one is available. By default they try the local development database server at 127.0.0.1:5433 with devuser / devpassword. To point them at a different server, set:
export INTEGRATION_TEST_CONNECTION="Host=127.0.0.1;Port=5433;Database=postgres;Username=devuser;Password=devpassword"
dotnet testThe test harness creates and drops isolated databases named integration_platform_test_*.
See docs/correctness-controls.md for the CI gates and review checklist used to keep feature work honest.