Self-hosting overview
Zupflow is open source under Apache-2.0. Everything the API offers runs from this repository: the server, the hosted Connect page and the Console. This section is for running it yourself; nothing here changes how your product talks to the API.
What you run
Section titled “What you run”The server is one binary, zupflow. It serves the API, the hosted Connect page and the background workers, and keeps its data in one SQLite file. The Console is a static web app that talks to the server.
flowchart LR C["Console<br/>static web app"] -->|HTTPS| Z B["Your backend<br/>SDK or REST"] -->|HTTPS| Z subgraph Z["zupflow (one binary)"] API["API"] CP["Hosted Connect page"] WK["Workers"] end Z --> DB[("SQLite file")] Z -->|HTTPS out| X["Model providers,<br/>MCP servers,<br/>webhook endpoints"]Every external resource (the database, the job queue, keys, the cache, model providers) is behind a trait, with a working implementation in the repository. A deployment that needs Postgres, a managed key service or a shared cache replaces them without forking. Architecture explains how the pieces fit, and Extending Zupflow how to swap one.
Try it locally
Section titled “Try it locally”This runs the Console in your browser and the server on port 8080, with a scripted model so you need no provider account.
Requirements
Section titled “Requirements”- Node 22+ and pnpm 10
- Rust, at the version pinned in
server/rust-toolchain.toml(rustup picks it up)
Run it
Section titled “Run it”-
Clone the repository and install dependencies.
Terminal window git clone https://github.com/zupflow-hq/zupflow.gitcd zupflowpnpm install -
Start the server. The
devscript useslocalidentity (no sign-in: you are the server’s built-in owner) and the scripted model.Terminal window ZUPFLOW_CONSOLE_ORIGIN=http://localhost:5180 pnpm --filter @zupflow/server dev -
In another terminal, start the Console against it.
Terminal window VITE_ZUPFLOW_API_URL=http://localhost:8080 pnpm dev -
Open http://localhost:5180, create an organization and a Sandbox workspace.
Then follow the Quickstart with http://localhost:8080 as your API URL. Locally, the scripted providers accept any key of the right shape (20 to 500 characters, no spaces), so you can connect one without an account. With the scripted model, a line call <tool> <json> in a run’s input calls a tool; anything else is echoed back. To try the full OAuth flow locally, use Taskboard, a fake MCP app: see Development.
The repository
Section titled “The repository”Directoryapps/
Directoryconsole/ the developer Console (Vite, React, TanStack)
- …
Directorydocs/ this site
- …
Directorypackages/
Directorycontract/ zod schemas, the route table and the generated
openapi.json- …
Directorycontract-tests/ the black-box suite every server must pass
- …
Directorysdk/ the TypeScript SDK
- …
Directorybrand/ logos and colors
- …
Directoryserver/ the Rust backend, a Cargo workspace
Directorycrates/
Directoryzupflow-core/ shared types and the traits for every external resource
- …
Directoryzupflow-basic/ the basic implementations (keys, cache, files, events, metrics)
- …
Directoryzupflow-sqlite/ the basic database and job queue
- …
Directoryzupflow-oidc/ Console sign-in through OpenID Connect
- …
Directoryzupflow-outbound/ the safe outbound HTTP path
- …
Directoryzupflow-mcp/ a minimal MCP client
- …
Directoryzupflow-providers/ OpenAI, Anthropic and OpenRouter adapters
- …
Directoryzupflow-channels/ the fake channel provider
- …
Directoryzupflow-slack/ Slack, for agent channels
- …
Directoryzupflow-conformance/ one suite per trait
- …
Directoryzupflow-server/ the API server, the
zupflowbinary- …
The contract is the source of truth. Every request and response is a zod schema in @zupflow/contract; the OpenAPI document, the SDK’s types and the Console’s types are all derived from it, and the same contract suite runs against the in-browser mock and the Rust server.