Features
The whole operator loop,
in one console
Ship a build. Watch it come up. Read the logs when it doesn't. Page someone. Prove what happened. Nine tools' worth of work, one login, one audit trail.
Deploy
Guided deployment, recorded and reversible
A deploy is a sequence — get the artifact, put it where it belongs, install the service, start it, check it came up. The panel walks that sequence for the environment you have selected, and writes down what it did.
- Three artifact sources — a local build, an S3 bucket, or a Jenkins job.
- Bulk service actions — start, stop, restart, install, uninstall one service or fifty, with live status.
- Deployment history — source, build, environment, user and result for every deploy.
- One-click rollback to the last successful build, straight from the dashboard.
- Custom launch arguments per service — JVM flags, non-jar commands, whatever the service needs.
# Jenkins / GitHub Actions, after a successful deploy body='{"envId":"qa","build":"214","source":"github-actions", "services":["gateway"]}' sig="sha256=$(printf '%s' "$body" \ | openssl dgst -sha256 -hmac "$CP_CI_WEBHOOK_SECRET" \ | awk '{print $2}')" curl -X POST "$PANEL/api/webhooks/ci" \ -H "X-CP-Signature: $sig" \ -H 'Content-Type: application/json' \ -d "$body"
start_services; the server builds the command from config. A client never sends a shell
string.Monitor & alert
Uptime, latency and an incident timeline you can hand to a post-mortem
Continuous HTTP and TCP health checks with history that survives a restart. Uptime over 24 hours and 7 days, latency sparklines, and a timeline of every down and recovery with its duration — and a failure that never reached the service kept separate from one that did, so the numbers mean something.
- Unreachable is not the same as down — a check that never reached the service (dial timeout, no route, DNS) is reported amber, not red: no incident, no alert, and excluded from the uptime percentage rather than counted as a failed sample. A dropped VPN stops looking like an outage.
- Corroborated, not guessed — one probe cannot tell a powered-off host from a lost route, so that verdict comes from agreement across targets in the same cycle: a
gatetarget for the bastion a service sits behind, arequiresVpnflag, or neighbours on one route going quiet together. A conclusive failure vetoes it — a target that returned 503 proves the route carries traffic. - Checks that find themselves — a service's Health tab probes
/actuator/healthand its usual neighbours and offers the first that answers, one click to start checking it. Monitor every service here does the whole environment in one press. - Checked from where the service actually lives — a remote environment's service is probed from inside that environment, over the same SSH route logs and restarts already take. A service behind a bastion or on a private subnet gets a real check instead of being reported down forever.
- Five alert channels — Slack, Microsoft Teams, PagerDuty, generic webhook and email, on down and on recovery.
- One alert per outage, not one per target — transitions are batched into a single notification (“2 down, 1 recovered, 3 unreachable”) rather than a pile of mail that is largest exactly when the outage is biggest. PagerDuty is deliberately left out of the batching, so each incident it raises still resolves itself.
- Email templates — concise, detailed, incident report, HTML summary, or a custom one started from any of them.
- Fleet dashboard — every target at a glance, beside the live audit activity feed.
- Prometheus endpoint —
/metricsexposes the same data to an existing Grafana stack, with a bearer token for the scraper. - Live host metrics — CPU, RAM and disk per environment, with your own warning and critical thresholds.
| Channel | Trigger | Target |
|---|---|---|
| Slack | down + recovery | #ops-alerts |
| PagerDuty | down · prod only | P1 service key |
| down + recovery | incident report | |
| Webhook | digest · 20s window | POST /hooks/cp |
Status board
A page you can hand to someone who will never open a console
Support, delivery leads, the person who gets asked “is it down?” before you do. One screen that answers that question and nothing else — no hostnames, no exit codes, no stack traces.
- Four states, in words — Working, Having problems, Down, Not checked yet. A colour and a sentence, not a status code.
- It cannot disagree with Monitoring — it is a read-model over the same store, not a second collection. There is one set of facts, shown two ways.
- Read-only, and safe to leave open — nothing on it starts, stops or deploys anything, so it is granted to every role including
viewer. - Says when it last looked, coarsely — “3 minutes ago” — because a stale green is the one thing a status page must never imply.
| Customer portal | Working | checked 2 min ago |
| Checkout | Working | checked 2 min ago |
| Reporting | Having problems | since 11:48 |
| Nightly export | Not checked yet | — |
Memory & heap dumps
Which process is holding the memory — and what is inside it
Two views over one reading: what an environment is holding, and what one service is holding inside it. Then, when the answer is “a JVM”, the dump that says which objects.
- Every process, biggest first, refreshed live, with the host's own total, in-use, available and swap beside it — and a by program roll-up, so forty database backends read as the gigabyte they are rather than forty unremarkable rows.
- Per service, measured the way that service actually runs —
docker topfor a container,kubectl top podfor a workload, and on a plain host the processes whose command line mentions it, labelled a match rather than a measurement, because nothing there marks where a service ends. - Command lines are redacted server-side before a process table reaches any browser —
--password=…,DB_PASSWORD=…, credentials inside a connection string — with the flag left intact so the shape survives without the secret. A process table is a classic way a password leaks, and this reading is open toviewer. - Kill is two steps, never one — the first press sends
SIGTERM, which a process can catch and flush; only one still listed afterwards offers Force. Operator-only, audited either way, and it refuses a pid below 2 or the panel's own process. - Heap dumps without a terminal —
jcmd(falling back tojmap), free space checked against the process first, and the file lands in the environment's ownheapdumps/directory, never/tmp, which on many hosts is the very memory being diagnosed. - Read the dump in the panel — class histogram, the largest single objects, and a GC root count, streamed straight through the parser with the bytes discarded as they are read. A 40 GB hprof costs a few megabytes and no disk, instead of a multi-gigabyte download and MAT on somebody's laptop.
- Automatic capture on a threshold, armed per service and admin-only. It fires once per breach and re-arms only when the reading falls back under, so a service parked above its limit produces one dump rather than one a minute until the disk is full.
- Ask the Assistant about it — Analyze with Assistant hands it the reading on screen (the by-program table, the host's figures and this session's trend) and gets back a ranked list of what to close, cap or leave alone. Every process or pod row has an Ask button too: what is this, is that much memory normal, and how to cap it safely. See the Assistant.
| java ×6 | 9.4 GB | |
| postgres ×41 | 7.9 GB | |
| node ×3 | 2.6 GB | |
| nginx ×8 | 412 MB | |
| redis-server ×2 | 288 MB |
Logs, files & shell
Twenty log tails, one search box
Select any number of services and follow them at once in a split-pane grid — twenty at a time is an ordinary afternoon, not the limit. Type once and the search highlights across every pane, with per-pane and total match counts and a case toggle.
- Stack traces fold themselves — repeated frames collapse to “11 more frames” and a shared tail to “common frames omitted”, so one exception does not push nineteen other services off the screen.
- Merge the panes into one interleaved stream when the question is ordering rather than isolation, and Stop all when it is not. Levels are badged, and a pane with errors in it says so on its own header.
- Download the whole log, not just the tail on screen — for the selected service, from the single pane or from any pane of the split grid.
- The full-screen grid is a view you can leave — park it on another tab and come back to it at the tail of where you were, with its followers still running rather than stopped.
- SFTP-style file explorer — browse, edit inline, upload, download, create, rename, delete, properties, and
chmodon Linux. Transfers reach Windows and POSIX hosts alike, directly or through a bastion, and a large upload goes in parallel chunks with timeouts, so one stalled chunk no longer hangs the lot. - Pooled SSH in the Go backend — one persistent connection, so directory browsing does not pay a handshake per click.
- A real web terminal, in split panes — a terminal emulator rather than a text box, so
top,lessandvimdraw properly. Split a tab right or down, or combine tabs into one, and each pane is headed with the environment it is connected to, so the PRODUCTION shell never passes for STAGING. Admin-only, audited, streamed over a WebSocket, to a host or into a container viadocker exec. - A shell in the containers on your own machine — on the Local environment, Exec into container opens the same terminal into a container Docker Desktop is running, on a local pseudo-terminal (ConPTY on Windows). The container only, never the host itself, and never offered by a panel serving a workspace over the network.
- Live console for ad-hoc commands against the selected environment, behind the admin role.
- Filesystem sandbox — reads and writes are confined to the app directory, the temp dir and configured roots.
| Name | Size | Mode | Modified |
|---|---|---|---|
| application.yml | 4.1 KB | 0644 | 12:04 |
| logback.xml | 1.8 KB | 0644 | Tue |
| gateway.jar | 88.2 MB | 0755 | 12:03 |
| secrets/ | — | 0700 | Mon |
Load testing
Load tests with a pass mark, and no JVM to install
Define a suite of steps — headers, bodies, assertions, and extractions that feed the next step — then say what load to drive and what counts as passing. The panel runs it, shows it live, and gives a pipeline a plain yes or no.
- Pass/fail criteria — p95 at most 800 ms, errors at most 1%, at least 50 req/s, for the whole run or one step. The verdict leads the report, and the run and its history carry a PASSED or FAILED badge. A stopped run never passes.
- From a pipeline, on a schedule, after a deploy —
cpctl loadtest run --wait --junitprints the verdict, writes JUnit XML and exits 1 on a miss. Or let the panel start a suite on a cron schedule or after every deploy to an environment, and send the result to the alert channels. - Load shapes and the open model — stages for ramps, holds and spikes, or a fixed arrival rate that starts iterations whether the target keeps up or not, counting the ones it had to drop. Weighted scenarios mix journeys: 70% browse, 20% search, 10% buy.
- Users that behave like users — each keeps its own cookies, takes its own rows of CSV test data and pauses a random think time, with
${__uuid},${__randInt(1,500)}and friends for the rest. - Watched live — requests and errors per second, average and p95 response time, and users against the profile, every two seconds, read from the run's own collector so it agrees with the report.
- Baselines, trends and the target's host — make any run the baseline and later reports mark each step worse, better or the same; a
maxRegressionPctcriterion fails a run that slipped too far. A suite can watch the target's CPU, memory and load during the run, on the same timeline. - Start from what you have — turn saved API client requests (with their environment, auth and token scripts) or a browser HAR recording into a suite, shown for review before it is saved.
- Reports to hand on — a self-contained HTML report (Apdex, per-step statistics, percentile curves) exported as HTML, PDF or a ZIP, plus JMeter-format
.jtlsamples. JMeter.jmxscripts still run, in the same run list and history.
// ramp to 200 shoppers, hold four minutes, ramp down "load": { "stages": [ { "durationSec": 60, "users": 200 }, { "durationSec": 240, "users": 200 }, { "durationSec": 30, "users": 0 } ] }, "criteria": [ { "metric": "p95", "max": 800 }, { "metric": "errorPct", "max": 1 }, { "metric": "p95", "maxRegressionPct": 10 } ]
cpctl loadtest run --suite "Checkout journey" \ --wait --junit report/junit.xml # exit 1: missed its criteria · exit 2: a mistyped command line
Databases
A universal database client for sixteen engines, already open
Relational, document, key-value, graph, search, time-series and wide-column — connect, query, edit rows in place, and move data between them without leaving the console.
Relational
- PostgreSQL
- MySQL / MariaDB
- SQL Server
- Oracle
- SQLite
Document & KV
- MongoDB
- Redis
- DynamoDB
- Firestore
- Couchbase
Analytics & search
- ClickHouse
- Elasticsearch
- InfluxDB
- Cassandra
Graph & multi-model
- Neo4j
- SurrealDB
Edit rows, not just read them
Writes are opt-in per session, and the grid says so before you touch anything: click a cell to edit
it, Ctrl+Delete sets NULL, the bin in the row gutter deletes a row. Behind
it, a generated WHERE key, safe result caps so a runaway SELECT never lands
in memory, and server time and round-trip on every statement.
Transfer between engines
Copy a table or a query result from one connection to another — including across engine families — with the schema mapped for you. A schema copy brings its views, routines and triggers too, and re-syncs sequences after the load so the next insert does not collide with a copied key.
Scheduled queries
Run a statement on a schedule and keep the output, for the recurring check that would otherwise be a cron job nobody owns.
A script runs as a script
Run breaks the editor into its statements and runs them in order down one session, with a tab per statement above the grid — its verb and its row count — instead of sending the whole buffer as one query and failing on everything after the first semicolon. A failure stops the run and names it (“Statement 2 of 5 failed”), leaving what already ran on screen.
Sessions that come back by themselves
An idle session is reaped after 30 minutes, and every call against a dead one used to end the tab, its editor buffer and its results. The first call that finds the session gone now re-establishes it, asks for the same session id back, re-applies the active database, and runs again — saying so once. Saved connections are a searchable list too, grouped by engine, with open ones lifted to the top.
Data in, in its own dialect
CSV import with a row limit and validation, so a large file can be sampled before it is loaded in
full. MongoDB takes mongosh's relaxed syntax — unquoted keys, single quotes, comments,
ObjectId(), ISODate() — as well as strict Extended JSON, and refuses
arbitrary JavaScript by name.
API client, tokens & CLI
Poke the services you manage, from where they live
A Postman-shaped HTTP client built into the dashboard: method, URL, query and path parameters, headers, auth (Basic, Bearer, API key, OAuth 2), and every body mode — form-data with uploads, urlencoded, raw JSON/XML/HTML/JS/text, binary, GraphQL.
- Run it from inside the environment — curl over the pooled SSH connection, so internal addresses resolve the way they do there.
- Scripts — pre-request and post-response, the
pmAPI in a sandboxed frame, writing variables for the next call. - Import and export — OpenAPI or Postman collections in, Postman collections out.
- Scoped API tokens — role- and environment-limited, revocable, expiring.
cpctlCLI and a documented JSON API at/api/docswith an OpenAPI spec.
# scoped token: deployer on qa only, expires in 30 days cpctl token create --role deployer --env qa --ttl 30d # drive a deploy from CI cpctl deploy --env qa --build 2481 --services gateway,search-svc # wait for health, then report cpctl status --env qa --wait-healthy --timeout 5m cpctl report summary --env qa --window 30d
curl -H "Authorization: Bearer $CP_TOKEN" $PANEL/api/openapi.json
Git
Every repository's pull requests, on one page
Compare branches, review and merge pull requests, and keep a folder of clones in step — on GitHub, GitLab, Bitbucket Cloud and Bitbucket Server / Data Center, hosted or self-hosted.
- Compare two branches, tags or commits: ahead and behind, the commits, every changed file and a unified or side-by-side diff — from a clone on the panel's host or from the provider. When head is ahead, Open pull request carries both over.
- A dashboard across every repository — filter by ticket, branch or author; each row shows whether it can merge, its CI and its approvals; approve, merge, decline or copy links for the ticked rows in one go.
- One change, many repositories — open the same pull request in every service that has the branch, with reviewers picked from the server's directory, and get the links back ready to paste to a reviewer. Or create one branch across all of them first.
- Workspace — every clone's branch, ahead/behind and uncommitted files, read without touching the index, with the exceptions lit. Fetch, fast-forward-only Pull, Push (never forced) and Switch over the clones shown, auto-fetch every 5, 15 or 30 minutes, and Clone missing for the repositories you do not have yet.
- Tokens stay on the server — stored encrypted, never sent to the browser. Every write asks first, says it acts as the token's owner, and is audited under your name; approving and merging need the deployer role.
Utilities
The tools people paste into public websites, kept on your machine
A drawer of small tools beside the operations ones. Everything except Git runs entirely in the browser: the tokens, contracts and recordings people would otherwise upload to a converter never leave the machine.
Encode / decode
Base64 (and URL-safe), Base32, hex, percent-encoding and HTML entities, both ways, over text or a dropped file — with errors that name the character and its position.
JWT decoder
Header, payload and a claims table that reads exp, iat and nbf
as times and says whether the token is in its window — and that the signature was not verified.
Image tools
Resize, rotate, flip, convert between PNG, JPEG and WebP, compress with before-and-after sizes, and remove a flat background by colour.
Icons & favicons
One image in, the whole set out: a real multi-size favicon.ico, Apple touch and PWA
icons, a ZIP of everything and the <link> block to paste.
PDF tools
Merge with per-file page ranges, organise from a thumbnail grid, split and compress — and Lock with AES-256 and print, copy or edit restrictions, or Unlock with the password.
Convert
Plain text, PDF and Word (.docx) in either direction, through a box of text you can see
and edit — and a scan is called a scan rather than handed back empty.
Audio tools
Find the moment by ear, trim it (or trim the silence), boost a quiet voice, resample, normalise — and save as WAV, FLAC, or G.711 µ-law and A-law for a phone system.
Remix
Up to twelve recordings on one timeline — end to end, stacked or crossfaded — with per-track trim, gain and fades. Auto remix cuts the dead air, matches levels and lays in the crossfades.
Assistant
An assistant that looks things up instead of guessing
Ask about the environments the panel manages — what is running, how much memory it is using, what a service's logs say, what shipped recently, who changed what. It answers from the panel's own endpoints and lists every lookup it made above the answer.
Your permissions, never its own
It calls the panel as the signed-in user, so it sees what you can see and nothing more. Read-only unless an admin enables its shell tool — and then its header says so — and it proposes changes for you to confirm rather than making them.
Off until an admin turns it on
No key, no assistant: nothing is mounted and nothing is sent anywhere. Once set up it speaks to the
Anthropic API or to a local claude CLI, and only the personas you allow see it.
Handed the context, not a blank box
On the Memory page, Analyze with Assistant sends the reading on screen and asks what to close, cap or leave alone; every process and pod row has an Ask button for the one you are looking at.
Environments
Agentless management for four kinds of target
Agentless throughout: nothing is installed on a managed host. Environments are defined once, and every screen in the panel — services, files, logs, metrics, console — follows the one you have selected.
Windows
The local machine, or a remote host over WinRM with RDP for interactive sessions. Services are scheduled tasks, and status, logs, start, stop, install and file transfer run in the panel's own Go code rather than helper scripts left on disk. WinRM credentials are stored with DPAPI.
Docker & Linux over SSH
Direct SSH or through a bastion / jump host. A pooled connection in the Go backend keeps file browsing fast, and container operations run as named actions built server-side. Fully Linux-native — no Windows host needed.
Kubernetes
A cluster driven with kubectl from a jump host, a master node, or the panel's own machine.
Services are Deployments; stopping scales to zero and starting restores the previous replica count.
The panel itself, anywhere
A single binary with the UI embedded, a Docker image, a docker-compose.yml, a Helm chart
with persistence and optional ingress, or a systemd unit for bare metal. On Windows and macOS it also
keeps itself up to date — see in-app updates.
Working across them
Attach your IDE to a running service
A JVM under debug listens for JDWP inside its container, on a private subnet, behind a bastion — three
walls between it and IntelliJ, and the usual answer is an ssh -J … -L 5005:… composed by
hand. The Debug tab reads whether the agent is actually listening (and calls out
suspend=y, and a loopback-only address=5005 that no forward can reach),
opens a forwarded port over the environment's own SSH route, and tells you what to point the IDE at.
Turning the agent on means recreating the container, so it is staged behind the same confirmation a
restart carries — and only where Allow debug restarts is enabled for that environment.
Two environments, side by side
The panel has one selected environment, so looking at staging used to mean losing the production view you were reading. Any environment now opens in a window or tab of its own from the ↗ beside its name — a real link, so middle-click, “Open in new tab” and bookmarking all work. Give each environment a colour in Settings and PROD reads unlike DEV before you have read a word; the reachability dot is never tinted, because health outranks identity.
Roles, reporting & fit
Different jobs see different consoles
Four roles, each including the last: viewer reads, operator starts and stops, deployer installs and writes files, admin owns settings, environments, credentials and the raw console. Enforced in Go on every route.
- Per-environment roles — lower someone's role on the environments you name: a deployer everywhere except production, where they may only read. An override can only lower a role, never raise it, so naming an environment in a request can never grant anything.
- Dashboard templates — Infrastructure, Developer, Operator, Administrator, or a custom arrangement you drag together. Admins set a default per role.
- DORA metrics — deploy frequency, change-failure rate, MTTR from the incident timeline, and 30-day SLA per target.
- Single sign-on via OIDC with PKCE — Okta, Entra ID, Google, Keycloak — mapping an IdP group to a panel role.
- Make it yours — app title and subtitle, an uploaded logo, colour themes from Dark and Light to Matrix or your own, and six layout styles — see Look & feel.
- Settings backup & clone — export a configured panel and stand up another one from it.
- In-app updates — a chip in the header when a newer release is out, and an Updates page with the installed version, the release notes and a release channel. On Windows and macOS an administrator can install it in place: the published SHA-256 is verified before anything runs, and a download whose hash does not match is deleted rather than kept. Everyone can see which version is running; only an admin can check, install or change the settings, because installing restarts the panel for everyone connected to it. Linux and container deployments report the release and link the download — there the running version belongs to your compose file or package manager, not to us.
| Role | Can | Cannot |
|---|---|---|
| viewer | read status, logs, files | run anything |
| operator | start / stop / restart | install, write files |
| deployer | deploy, edit files | settings, credentials |
| admin | everything | — |
Look & feel
Your console, your way
Six styles, each a different idea of how the same panel should be laid out — and a keyboard way round all of them.
- Six styles — Console, the default: a dark console tuned for Database, Git, Memory and Utilities. Graphite, with the sidebar in foldable groups. Daylight, light with a rail. Ops Deck, with menus along the top and no sidebar. High Contrast, at 7:1. And Terminal: monospace, dense, palette-first.
- Auto theme, accents and density — follow the operating system's light or dark, pick one of six accent colours, and go compact to fit more environments and services on a screen. Each person's choice is kept in their own browser.
- A default for everyone — an admin sets the panel's style and one per persona — developer, DevOps, DBA, QA, manager — and anyone who picked their own keeps it.
- Command palette —
Ctrl K(⌘Kon a Mac) jumps to any page, Settings tab or environment, or runs an action, from a few letters. - Wallboard —
/?wallboard=30turns a spare screen into a NOC view that cycles Overview, Status and Monitoring on a timer, with pause, full screen and exit.
Ctrl K: pages, settings, environments and actions, matched as you
type.
Desktop extras
Three things only a panel on your machine can do
The phone is on a USB port, the disk is the one under your desk and the desktop is the one in front of you — so these belong to a panel on your own machine, and are never offered by one serving a workspace over the network.
Disk cleanup
Works the way a desktop cleaner does: tick categories, Analyze to measure, then Clean what was measured. Two groups — the panel's own output (load-test samples, HTML reports, rotated logs, Playwright artifacts) and the OS's (temp directories, Recycle Bin, update caches, crash dumps, thumbnail and browser caches, with Linux and macOS equivalents).
- A scan deletes nothing
- The client names a category, never a path
- Counts only what it can free: without Administrator, those categories are marked and left out of the total
- OS categories require admin; the live audit log is never a candidate
Import from phone
Copy photos, videos and contacts off a phone into a folder on the panel's machine. Pick the device, browse its folders, filter by kind, import — a repeat run behaves like a sync and skips what is already there, matched by SHA-256.
- Android over USB, or wireless — paired by scanning a QR code
- iPhone via libimobiledevice; on Windows, the built-in Portable Devices namespace
- Every import audited with its counts
Desktop recording
Record the Windows desktop the panel runs on as an MP4 that keeps going until you press Stop
(or Ctrl+Shift+R), with scaled displays captured in full and a lock
screen paused rather than recorded — or take a desktop snapshot. Recording the browser tab, as MP4 or
WebM up to ten minutes, works wherever the panel does.
- Desktop capture needs admin, and only someone at that machine is offered it
- Recordings and snapshots are kept beside the screenshots
See it against your own environment
A free workspace takes a minute to create, and the self-hosted binary needs nothing but a folder to sit in.