Skip to content

Releases: albertorsesc/planeops

v0.11.2

Choose a tag to compare

@github-actions github-actions released this 19 Aug 21:47
d48bf26

Added

  • Every drift item carries the intent its entry was declared with, and the
    kill_criteria where one is set, rendered under the item in DRIFT.md and
    as fields in DRIFT.json. The registry has always required a reason per
    entry and no output had ever shown one, so a report could name a missing
    secret while the sentence explaining what it is for, and that the remedy is
    migrating it into the sops store, sat two files away. An item the ungoverned
    pass built from an observation has no declaration behind it and carries
    neither. DRIFT.json moves to schema 3 for the new fields; the terminal view
    is unchanged, since packing same-message groups onto shared rows is what
    keeps it scannable.

v0.11.1

Choose a tag to compare

@github-actions github-actions released this 19 Aug 20:51
8a042a8

Added

  • unmanaged.yaml takes a publishers: list beside globs:, so a vendor can
    be exempted once instead of one agent at a time. A publisher matches an
    identity the adapter attested rather than a name the subject chose: on macOS
    the launchd adapter reports the Team ID its program is signed with, so a
    single line covers everything that vendor ships, including agents that do not
    exist yet. That is also why it is safe where a com.vendor.* pattern is not,
    and why it may cover a service that runs at login.

    An agent running /bin/sh -c ... cannot inherit one: the OS signs its own
    binaries without a Team ID, so no interpreter carries a publisher and no
    publisher rule can silence one. Exceptions need no syntax, since a declared
    entry is never exempt: declare the one asset you do want governed.

    A rule covers what can be attested, so an agent that declares no program has
    nothing signed and still needs a glob: on one real machine two of a vendor's
    four agents were covered by one publisher line, and the two on-demand helper
    plists with no program were not.

    Publishers are macOS-only for now. systemd units are not signed, and package
    ownership does not carry the same guarantee, so the systemd adapter attests
    nothing and a publishers: rule simply never matches there.

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 19 Aug 19:20
4da5599

Security

  • BREAKING: An unmanaged glob could hide a service that runs code at
    login. Matching items were dropped before the snapshot was written, so the
    check that alerts on an ungoverned always-on service never saw them. The
    identity a launchd glob matches is the Label inside the plist, which
    whoever writes the file chooses, so a wildcard exemption such as
    launchd/com.vendor.* was a name anything could adopt to run at login
    unmentioned. A glob now withholds the report's question and not the
    observation, which is what SPEC section 2 already described, and a pattern no
    longer covers an always-on service at all. Naming one exactly still does, so
    a vendor updater you have looked at stays quiet while the name space around
    it does not. Migration: an always-on service a pattern used to cover now
    alerts until it is named exactly in unmanaged or declared in the registry.
    The snapshot moves to schema 2, which adds unmanaged; plane observe
    rewrites it, so nothing carries over.

Changed

  • Exempted items now reach the snapshot, so plane observe and the MCP
    inventory tool count them: their totals rise by however many an unmanaged
    glob covers. The inventory gained unmanaged_count so the number explains
    itself rather than looking like a jump in what the machine runs.

Fixed

  • A glob that also matched a declared entry reported an installed asset as
    missing. Exemption is skipped for anything declared, so the two registry
    files can no longer contradict each other into a false alert.
  • plane mcp counted an exempted server as ungoverned, and
    plane import observed proposed exempted items now that they reach the
    snapshot. Both honour the exemption, except for an always-on item, which
    import observed still proposes because declaring it is the remedy the
    report names.
  • A backup file stopped being debris the moment it was dated. footprint
    skipped .zshrc.bak but asked about .zshrc.bak-20260727,
    .zshrc.bak.pre-qwen8b, and .openclaw.archive-20260722, because the
    default patterns matched the bare suffix and nothing after it, while the way
    anyone actually names a backup is to append what it was taken for. Those
    three spellings are filtered now, each pattern anchored on the separator
    that follows the backup word so a real tool called bakery or archivebox
    is never silently skipped.

v0.10.4

Choose a tag to compare

@github-actions github-actions released this 12 Aug 03:22
1c4e9e2

Fixed

  • An adapter that misspells one of the six facts the triage reads (present,
    drifted, always_on, stale, configured, governed_by) no longer scans
    clean. The facts map is open by design, so alwayson was not an error, it
    was simply a fact nobody reads: the service still ran at login and nothing
    said so. Adapters now state those six as named arguments to Observed.of,
    where a misspelling is a type error at the line that wrote it, and observe
    re-checks them as it collects, which is what covers an adapter the engine
    never type-checked. A general fact of the wrong type is refused for the same
    reason, since bool("no") is true and a present of "no" reads as the
    opposite of what it says. Everything an adapter records of its own is
    untouched, present_at and configured_by included, and a refusal lands in
    the snapshot's failed list, so one bad adapter cannot sink a scan.

v0.10.3

Choose a tag to compare

@github-actions github-actions released this 11 Aug 19:29
366c739

Added

  • plane init --sections prints the documented instance.yaml sections your
    instance does not set, ready to append (>> instance.yaml). An instance
    created before an adapter existed never heard that the adapter has a
    section, because the starter file is written once and then left alone; the
    only hint was diffing the shipped example by eye. Re-running plane init
    on an existing instance now names the unadopted sections too, so the
    command you already know is what tells you. It writes nothing: the file is
    yours, and a config that edits itself is one you no longer know the shape
    of.

Fixed

  • Every prompt now answers the same way when there is nobody to ask. Nine
    sites each caught their own idea of a missing stdin, and none caught the
    RuntimeError Python raises when the descriptor itself is gone, so a verb
    run without stdin ended in a traceback rather than a decline. They share
    one helper: an answer comes back from a terminal or a pipe, and None
    comes back when stdin has ended, is detached, or is closed, which each
    caller turns into its conservative branch. Piped answers keep working
    deliberately, since plane apply has no flag that would replace them.

v0.10.2

Choose a tag to compare

@github-actions github-actions released this 11 Aug 01:55
30f7b94

Added

  • A harness adapter: code plugged into an AI harness, starting with hooks.
    Profiles are configuration (harness.profiles in instance.yaml); which
    file holds a tool's settings, and how that file's schema names its hooks,
    lives in a harness leaf, so the adapter names no tool and supporting
    another is dropping a module in. A hook runs code unprompted, so it
    observes always_on and an undeclared one alerts through the pass that
    already catches self-installing daemons. Identity is kind, event, and the
    script it runs; the command string is never recorded, because a shell line
    can carry a token and the script path is the identity anyway. present
    means the hook will actually run, so one whose script is gone is wired but
    broken, and resolvability is the test rather than the executable bit (a
    script passed to a runtime is normally not executable). Each hook records
    the harness that runs it and the config dirs that wire it, so several
    profiles of the same tool stay distinguishable.

v0.10.1

Choose a tag to compare

@github-actions github-actions released this 10 Aug 22:45
d537d93

Changed

  • CI checks a release's version bump against its own changelog section, so
    the numbering rule is enforced rather than remembered: a BREAKING entry
    must move the slot reserved for it (the minor while 0.x, the major from
    1.0), and moving that slot without one is refused. scripts/check_version_bump.py
    runs the same check locally. Releases through 0.10.0 bumped the minor for
    features before the rule was written down; they are grandfathered, and the
    check applies from 0.10.1 on.

Fixed

  • The version guard reads a declared break, not the word: it looks for the
    bolded entry prefix CONTRIBUTING mandates (or the commit-footer spelling)
    rather than any occurrence of "BREAKING". A bare substring match meant
    release notes that merely described the rule were read as declaring a
    break, which refused the very release introducing the guard.
  • An active entry whose adapter reports present: False now alerts "expected
    present, not observed", the same as observing nothing at all. Only the
    retired path consulted that fact, so a booted-out service the adapter had
    looked at produced a soft report at most, and with tolerance: auto it was
    folded into silence: the one thing the loop exists to catch. The alert is
    structural, so tolerance cannot fold it, and the soft signals below it are
    skipped because they describe a thing that is not there. A parked entry
    stays silent (dormancy is its expected state) and an unconfigured active
    secret keeps its own more specific message.

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 10 Aug 01:45
dfdca3d

Added

  • plane drift groups each section by the observing adapter, states a shared
    message once on the group header, and packs the bare ids into columns sized
    per column. A 16-item section reads as four rows instead of sixteen, and
    nothing is truncated where it used to be. The grouping comes from the id's
    own adapter/native_id shape, so every adapter, present and future, gets it
    without the renderer naming any of them.

Changed

  • The footprint conventions the example instance recommends no longer include
    macOS's ~/Library/Application Support. Measured on a real machine, 37 of
    its 79 entries were the system's own and most of the rest were desktop apps
    and derived caches of tools a package manager already governs, so the root
    produced rows nobody can declare, apply, or remove. docs/footprint.md
    records why the alternative, teaching the tool to filter the OS's own
    directories back out, was rejected: that filter would key on names its
    subject chooses.

Fixed

  • The footprint noise defaults skip shell completion caches
    (.zcompdump, .zcompdump-<host>-<version>), found by running the
    adapter against a real machine. Two rules now bound what ships as a
    default: a pattern must match something real on a scanned machine, and no
    pattern may match a directory where software wires itself to run at login
    (systemd, autostart, LaunchAgents), which a test enforces.

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 09 Aug 21:31
f37eedd

Added

  • A footprint adapter: tools discovered by the config traces they leave.
    Roots are configuration (footprint.roots in instance.yaml: XDG config,
    data, and state, home dotfiles via dot_only, per-OS conventions via
    os:); the same tool across roots merges into one observation with a
    footprint per trace, and a configured root is never itself a tool. A tool
    whose name matches another adapter's declared entry is attributed to it
    (governed_by, a new general fact the triage honors) instead of listing
    as ungoverned, so discovery only asks about tools with no decision on
    record. Debris never becomes a question: OS artifacts, shell and editor
    state, backup copies, and the cache dir are skipped by name
    (ignore: extends the list, ignore_defaults: false drops it). All of
    it stat-only: nothing is ever opened, so credential-bearing configs
    contribute their name and shape, never their contents; a root the scan
    cannot list refuses loudly into the failed-scan alert, naming itself,
    instead of silently shrinking coverage. Opt-in: no section, no scan.

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 09 Aug 01:54
d6bd2e3

Added

  • The mcp adapter grows its write side: with manage: true under mcp: in
    instance.yaml, plane apply proposes removing a retired server's block
    from each client config that still wires it, behind the usual per-change
    confirmation. The edit is digest-guarded (refused if the file changed since
    the preview, or if its formatting does not round-trip byte-identically),
    atomic through symlinks with permissions preserved, and preceded by a
    0600 backup of the removed block under ~/.local/state/planeops/backups/.
    Project- and repo-scoped wirings are skipped by name; previews, results,
    and the journal never carry env values. Wiring servers in stays manual:
    env blocks hold secrets.
  • Observation records structured wirings per server (client + scope), so the
    write side targets exactly what was seen instead of re-deriving it.

Fixed

  • A parked entry no longer reports the drifted fact: a parked RunAtLoad
    service that is unloaded deviates from its own definition on purpose, and
    apply plans nothing for parked, so the report could only nag forever.
  • Changes executed under a standing a (all in domain) render their diffs
    before executing, instead of mutating with the diff only journaled after
    the fact. a answers the question for the domain, never the showing.