Pushleft
Pushleft is an open-source deployment platform for small teams: run pl up in a checkout and receive an isolated URL, without authoring a CI pipeline or provisioning infrastructure.
The repository now contains the first implementation slice:
pushleft-core—.pl.yaml, DNS preset, private-network, observability, and typed database contracts.pushleft-api— Rust control plane backed by PostgreSQL, with idempotent writes and resumable SSE events.pl— CLI for plan, deploy, list, stream, and teardown workflows.pushleft-agent— fail-closed host checks, secure Headscale enrollment, node telemetry reconciliation, and typed PostgreSQL/ClickHouse host management.pushleft-mesh— isolated Headscale coordinator runtime, HTTPS route publication, short-lived Vault-backed enrollment, and durable mesh job reconciliation. See coordinator operations.pushleft-agent-gateway— mandatory-mTLS node identity, targeted leases, heartbeat fencing, and append-only results.pushleft-telemetry-gateway— telemetry-only mTLS identity, OTLP protobuf validation, node binding, rate limits, and loopback collector forwarding.pushleft-scheduler— capacity- and capability-aware placement that expands database intents into pinned typed tasks.pushleft-openprovider/pushleft-cloudflare/pushleft-controller— quoted domain registration, durable provisioning, nameserver delegation, and owned-record DNS reconciliation.- React/Vite developer console — deployment operations plus customer domain checkout, progress, expiry, and DNS management.
Start with the handbook: architecture in one screen, infrastructure, how to run it, and how work on it is verified.
Run the developer stack
Herdr workspace
The shared launcher is ../tmux/bin/herdr-pushleft, following the other local
Herdr launchers. Run herdr-pushleft to create or focus its workspace, or use
herdr-pushleft --no-run to create only the pane layout. PUSHLEFT_ROOT can
select another checkout. The runtime tab has web (:4174), API (:8080), scheduler
and PostgreSQL (:55432); the tests tab has manual test commands and a CLI shell;
the Claude tab has the standard two Claude Code panes. Tuli keeps :5173.
Each pane runs its own entrypoint: dev/web, dev/api, dev/scheduler,
dev/docker, dev/cli or dev/tests. Up-arrow and Enter reruns that process.
dev/setup creates a private, ignored .env.herdr with a random local
token and keeps an existing file unchanged. It does not load .env or enable
registrar/Cloudflare credentials. Source ingestion uses ignored .local/herdr
storage; the configured registry origin is metadata only, not a running builder.
Cloud controllers, mTLS gateways and privileged host agents are not started.
Existing web/API listeners are reused, not killed; an independently started API
may have a different token. Set the browser session token as described below.
Tests are not started automatically. Use dev/tests, then choose
dev/test-unit, dev/test-web, dev/test-source, dev/test-all or dev/security. Integration
suites should run one at a time. Security scans still enforce the existing
runtime-image release gate; this launcher does not bypass it.
The shared implementation remains in scripts/dev; its older subcommands still work.
Manual startup
Requirements: Rust 1.88+, Node 20+, Docker with Compose.
cp .env.example .env
# Replace both example token values with the same 32+ character random token.
docker compose -f compose.dev.yaml up -d
set -a; source .env; set +a
cargo run -p pushleft-api
In a second shell:
set -a; source .env; set +a
cargo run -p pushleft-cli --bin pl -- plan
cargo run -p pushleft-cli --bin pl -- up
npm install
npm run dev -- --port 4174
The dashboard is at http://127.0.0.1:4174. Tuli keeps port 5173.
To show local API data in the browser without putting a development credential in the bundle, set the token for the current tab and reload:
sessionStorage.setItem('pushleftDevToken', 'your-local-token'); location.reload();
Verify
The complete suite requires local Docker, OpenSSL, Python 3, jq, and Caddy
(PUSHLEFT_CADDY_BINARY can select a binary). It includes real isolated
Headscale enrollment and restart/recovery tests. CI pins Caddy by checksum.
cargo fmt --all -- --check
cargo test --workspace
npm run build
npm run test:e2e
npm run test:agent-gateway
npm run test:scheduler
npm run test:database-agent
npm run test:network-agent
npm run test:mesh-controller
npm run test:mesh
npm run test:telemetry-gateway
npm run test:web
npm run security
# Or run all unit/integration/browser suites together:
npm run test:all
Architecture and security invariants are in docs/architecture.md. Domain provider setup and the billing/renewal boundary are in docs/domain-registration.md. Agent-managed database behavior is in docs/managed-databases.md. The approved interface rules are in docs/design-baseline.md. The review plan remains at output/pdf/pushleft-product-architecture-plan.pdf.
The target deployment contract uses Openprovider registration with Cloudflare authoritative DNS/proxying, one private management tailnet plus a dedicated Headscale workload tailnet per environment, 80/443 as the default public ingress, OpenTelemetry collection into ClickHouse with 90-day retention, cookieless web analytics, and agent-managed PostgreSQL/ClickHouse operations. Domain registration and Cloudflare DNS execution, mandatory-mTLS agent leasing, capacity-bounded database placement, typed database execution with XFS project-quota disk enforcement, authenticated node telemetry, and placement-bound privacy-reduced access ingestion are implemented. The environment-gateway contract, constrained Docker/Tailscale reconciliation, scoped nftables apply/readback, persistent firewall generation fencing, isolated Headscale runtime/HTTPS publication, and scoped enrollment-key recovery are implemented. Attachment/route orchestration, host-wide input/provider firewall policy, and recurring host verification remain unfinished; production admission stays closed. Other release work includes payment funding and renewals, automated fleet enrollment, application runtime execution, analytics-route publication/querying, database backup/PITR, HA, execution of the Linux/XFS release gate, and promotion of pinned runtime images that pass the HIGH/CRITICAL image policy.
Security posture
Network admission is enforced per environment and host at both placement and agent leasing. It requires fresh gateway, firewall, and route verification for the same desired generation and separately hashed policies. Gateway health checks verify the expected specification, ownership, bridge, and IP; readiness stays closed until the remaining controllers publish and maintain that evidence. npm run test:network-agent exercises stale and replaced gateways using real Docker containers. npm run test:linux-firewall and npm run test:linux-routes test real packets, routing-policy shadow detection, ownership and persistent fences in disposable Linux namespaces. The route test simulates the decrypted mesh link, not actual multi-host Tailscale transport. The GitHub verification workflow schedules these and the real XFS quota gate on a Linux runner; the quota gate cannot pass on the local Docker Desktop kernel, which lacks XFS quota support.
Local development uses an explicit static bearer token and refuses to start without it. This is not the managed authentication design. Production will use OIDC/PKCE for people, scoped service credentials for automation, and short-lived mTLS identities for agents. Secret values never belong in .pl.yaml; only vault:// and local env:// references are accepted.
License: AGPL-3.0-or-later.