Install · Quickstart · What you can do · Docs · Spec
planeops is an inventory and drift detector for the AI tooling on your machine: coding assistants, MCP servers, local models, background services, API keys. You declare what should exist and why in plain YAML; planeops tells you when reality disagrees, and changes nothing without showing the diff and asking first.
$ plane status --short
drift:2
$ plane drift
✗ 2 alert(s) on mymac 16:20
alerts (2)
✗ launchd/ai.gateway ungoverned always-on service; declare it or add an unmanaged glob
✗ ollama/qwen3:8b expected present, not observed
full report ~/planeops/observed/mymac/DRIFT.md
A machine with six things running that nobody wrote down, caught by one read-only scan.
Why
Your machine accretes AI tooling: coding harnesses, MCP servers wired into three different clients, local models, background services, API keys in dotfiles. Nobody writes down what is installed, how it is wired, or why it is there. Six months later, something is listening on a port and you cannot say what put it there.
planeops turns the pile into a registry: every asset declared with its reason for existing, every scan diffed against that intent, every fix a rendered change you confirm one at a time.
What you can do with it
| You want to | planeops gives you |
|---|---|
| Put your whole AI setup in writing | A plain-YAML registry: every service, model, package, MCP server, key, and config trace, each with the reason it exists. plane init --seed drafts it from what's already installed; you prune instead of authoring. |
| Find out what's really on the machine | plane observe: one read-only scan across package managers, launchd/systemd, Ollama, MCP client configs, secret stores, and the config traces tools leave behind, covering tools nothing else tracks. |
| Hear about it when reality diverges | plane drift: the daemon that installed itself, the model a cleanup pruned, the MCP server wired into one client but missing from the rest, the key that was never configured. |
| Converge without surprise mutations | plane apply: each fix rendered as a diff and confirmed one change at a time; nothing writes behind your back. |
| Pause a project without months of nagging | parked means dormant on purpose, so silence is correct; a completed retirement asks you to clean up the registry instead of alerting forever. |
| Keep API keys sealed and findable | plane secrets add/list/remove: names and presence tracked in the open, values encrypted at rest (sops + age), written only to the file their entry declares. |
| Make the loop ambient | plane schedule puts the scan on your OS's own timer, no daemon involved; plane status --short drops the state into your shell prompt. |
| Let your assistant work from real state | plane-mcp: read-only MCP tools for drift, status, the server-by-client view, and secret names, so "what changed on my machine?" gets a real answer. |
The guarantees under all of it: observation never writes, there is no daemon
and no open port, a registry typo fails at load instead of silently meaning
nothing, exit codes are scriptable (0 clean, 1 operator error, 2 drift),
and the engine names no vendor: adapters, schedulers, and secret stores are
all discovery seams.
Install
$ uv tool install planeops # or: pipx install planeops / pip install planeops
Installs the plane command. Python 3.12+, macOS and Linux.
Want your AI assistant to query the plane over MCP? Install the extra:
uv tool install "planeops[mcp]" (adds plane-mcp).
From source (development)
$ git clone https://github.com/albertorsesc/planeops
$ cd planeops
$ uv sync # engine + plane CLI
$ make check # the full gate: lint, format, types, tests
Quickstart
# scaffold an instance and seed the registry from what's already installed
$ plane init --seed
create the instance at /Users/you/planeops? (path or Enter to accept)
instance ready at /Users/you/planeops
wrote 73 entries to /Users/you/planeops/registry/imported.yaml; prune, then `plane drift`
# scan the machine, diff it against the registry
$ plane observe
✓ observed 73 facts on mymac /Users/you/planeops/observed/mymac/snapshot.json
brew 21 · mcp 14 · ollama 9 · npm 8 · launchd 6 · ...
$ plane drift
✓ no drift on mymac 16:04
# keep it fresh: an OS timer, previewed and confirmed
$ plane schedule --every 6h
# one glance forever after (empty means clean; wire it into your prompt)
$ plane status --short
drift:3
From there, plane apply walks the drift one confirmed change at a time.
plane init created an instance: a directory that is yours, not the
tool's. Git it like a dotfiles repo:
~/planeops/
├── registry/ desired state: the YAML you declare, edit, and prune
├── instance.yaml this machine's adapter settings
├── secrets.sops.yaml encrypted values, if you use the secrets store
└── observed/<host>/ generated per machine: snapshot.json, DRIFT.md
registry/ and instance.yaml are your setup's documentation; observed/ is
regenerated by every scan. Layout, multi-machine use, and the tool's exact
footprint: docs/instance.md.
Going further
- SPEC.md: the architecture, entry schema, adapter contracts, and exit codes.
- docs/instance.md: your instance directory, several machines on one registry, the tool's footprint.
- docs/secrets.md: the secrets flow (declare,
secrets add/list/removewith the store bootstrapped on first use,applymaterializes) and how values stay sealed. - docs/mcp.md: every MCP server across every client in one view, the opt-in unwire of retired servers, and the read-only server your assistant can query.
- docs/footprint.md: discovering tools by the config traces they leave, and how debris and already-governed tools stay out of the way.
- CHANGELOG.md: releases and what is coming.
What planeops is not
- Not a runtime. It never sits in any request path and starts no long-running process.
- Not an installer. Adapters shell out to the tools you already trust (
brew,systemctl,ollama); planeops decides whether, they do how. - Not a fleet manager. One human, their machines, their intent. Multi-host is on the roadmap as bundles of the same registry, not an agent mesh.
Status
Pre-1.0: the loop, twelve adapters, scheduling, secrets, importers, and the MCP server work on macOS and Linux and govern this project's own machines daily. Contracts may still move; a breaking change bumps the minor and lands in the CHANGELOG with its migration.
Built on
planeops delegates instead of reinventing: sops and age hold the secrets, chezmoi reproduces config files, your OS's own scheduler runs the ambient loop, and the package managers you already use keep doing the installing. The engine rides on ruamel.yaml for the registry, Rich with rich-argparse for the console, and the MCP Python SDK for the optional server, and is built with uv, ruff, mypy, and pytest. Thanks to all of them.
Contributing, security, license
CONTRIBUTING.md
has the dev setup, the quality gate, and how to write an adapter. Security
posture and reporting:
SECURITY.md.
Licensed Apache-2.0.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file planeops-0.9.0.tar.gz.
File metadata
- Download URL: planeops-0.9.0.tar.gz
- Upload date:
- Size: 261.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e482474403c481e8b6e26e2e4d60687227381a7910af48c0a37ac776e85fc11
|
|
| MD5 |
a713cf0216cb6fdd24e3e694f81da012
|
|
| BLAKE2b-256 |
9502222da43fd4c8d67af840680003d6738f64ac7720dd10bf042f6186ef0c5d
|
Provenance
The following attestation bundles were made for planeops-0.9.0.tar.gz:
Publisher:
release.yml on albertorsesc/planeops
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
planeops-0.9.0.tar.gz -
Subject digest:
8e482474403c481e8b6e26e2e4d60687227381a7910af48c0a37ac776e85fc11 - Sigstore transparency entry: 2399995907
- Sigstore integration time:
-
Permalink:
albertorsesc/planeops@f37eedd3939f6b74884377d8c862f42fd4b33d92 -
Branch / Tag:
refs/tags/v0.9.0 - Owner: https://github.com/albertorsesc
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f37eedd3939f6b74884377d8c862f42fd4b33d92 -
Trigger Event:
push
-
Statement type:
File details
Details for the file planeops-0.9.0-py3-none-any.whl.
File metadata
- Download URL: planeops-0.9.0-py3-none-any.whl
- Upload date:
- Size: 135.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5f76b155b8c413d63ea8a2f560430a91ae2de262c384368c94ad1f146cc0ffd2
|
|
| MD5 |
5af82c4d92351c65ee46fccbc067042e
|
|
| BLAKE2b-256 |
f648fb97837de6acf1e53cf5eddff7905d1e658b2b62e91e3a6de3027d8f3316
|
Provenance
The following attestation bundles were made for planeops-0.9.0-py3-none-any.whl:
Publisher:
release.yml on albertorsesc/planeops
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
planeops-0.9.0-py3-none-any.whl -
Subject digest:
5f76b155b8c413d63ea8a2f560430a91ae2de262c384368c94ad1f146cc0ffd2 - Sigstore transparency entry: 2399995957
- Sigstore integration time:
-
Permalink:
albertorsesc/planeops@f37eedd3939f6b74884377d8c862f42fd4b33d92 -
Branch / Tag:
refs/tags/v0.9.0 - Owner: https://github.com/albertorsesc
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f37eedd3939f6b74884377d8c862f42fd4b33d92 -
Trigger Event:
push
-
Statement type: