Skip to content

lever v0.5.0

Choose a tag to compare

@stevegeek stevegeek released this 16 Jul 08:30

Added

  • lever version now appends build provenance to the release string when the
    binary carries it: the commit it was built from (short) plus -dirty for an
    uncommitted tree (any go build / make install from a git checkout), or the
    module version for a go install …@vX build. A make-install binary that lags
    its source no longer hides behind the bare hardcoded version string.
  • lever init --adopt: record owner-customized scaffolds (the operator/agent
    SKILL.md files and the whole tree-root CLAUDE.md) as an accepted baseline in
    .lever-state/skills-adopted.json. Doctor's "operator skills" check and
    lever init --check then treat the adopted content as OK, and a plain
    lever init leaves it alone (including not appending the CLAUDE.md block).
    Previously a customized scaffold read skipped-modified forever. Adoption
    is deliberately a recorded baseline rather than a mute: the scaffolds live
    inside the agent-writable tree, so the check doubles as tamper detection —
    any change PAST the adopted baseline fails doctor as "modified since
    adoption", and the baseline itself lives host-side where an agent cannot
    re-bless its own edits. Only genuine customizations qualify: framework-
    current files and stale-but-unmodified scaffolds (plain-init refresh
    territory) never get a record. lever init --force still restores the
    framework content — for CLAUDE.md, by re-ensuring the marker block in place
    — and clears the now-stale adoption record.

Fixed

  • lever up no longer needs a second run to clear the first-boot
    start-manager race on a cold VM. The scion workstation daemon registers its
    runtime broker asynchronously after its Hub API comes up, so the first
    create/resume could act before the broker was ready — failing the apply (on a
    cold VM as a hub "context deadline exceeded") so only a second up
    reconciled. start-manager now waits for the runtime broker to be registered
    and online (via scion hub brokers) before acting, closing the window at the
    source; the wait is fail-soft, so it can never fail the bring-up on its own.
    As a backstop, the transient-broker retry also now treats a hub "deadline
    exceeded" as the same race, and the initial observe scion list rides that
    bounded, ctx-checked retry instead of failing on the first blip.
  • lever destroy now clears the persisted controller PAT
    (.lever-state/controller.pat). The PAT is minted against the hub DB that
    lives inside the jail, so destroying the machine leaves it stale; the next
    lever up reused it (ensureControllerPAT no-ops when a PAT is already
    persisted) and the new hub's fresh DB rejected it, failing the readiness
    probe with "authentication failed" until the file was removed by hand. Only
    the current-instance teardown (no --machine) clears it, alongside the
    broker stop and staged-ticket cleanup it already does.
  • lever apply no longer re-imports every agent image into the jail on each
    run. The load-image step now first compares the jail's podman image ID
    against the host docker image ID and skips the multi-GB docker save | podman load re-stream when they match (the config digest is stable across
    save/load, so equal IDs mean the exact bytes are already present). The check
    is fail-open — any uncertainty (a not-yet-loaded or rebuilt image, an inspect
    failure) falls through to a load, so it can never wrongly skip and leave a
    stale image. This matters most under the first-boot retry loop, where any
    step failing re-runs the entire plan: previously each retry re-streamed
    every image; now unchanged images are near-no-ops. After a load, the step also
    prunes dangling (untagged, unreferenced) jail images — so a rebuilt image,
    whose old copy the load leaves untagged, no longer ratchets the grow-only jail
    disk up by a full image size (a no-op when nothing was superseded). Pruning
    never touches a tagged or container-referenced image.
  • A tool whose broker backend carries a path (e.g. qmd's [::1]:3101/mcp) now
    reaches that path exactly on the tool root, instead of a trailing-slash
    variant (/mcp/) that a strict streamable-HTTP endpoint 404s. The trailing
    slash was an artifact of the broker's subtree mux; qmd was the only tool with
    a path-suffixed backend and so the only one that couldn't connect. Path-less
    backends (every other tool) and sub-path requests are unaffected.
  • lever doctor's "agent certificate" check no longer cries wolf right after
    a healing restart: an expired-leaf rejection logged before the current
    broker started (pid-file mtime) is reported as healed rather than as an
    active failure. Previously any rejection inside the 15-minute window failed
    the check even when the restart that fixed it had already happened.
  • The broker's mTLS serving cert now self-rotates. It was minted once at
    startup with the 24h leaf TTL, so a broker running longer than a day served
    an expired cert and every gateway handshake failed — tools down, and the
    agents' own /renew calls failed with it, so their leafs decayed too (the
    only remedy was a lever stop && lever up power-cycle). The broker holds the
    CA key, so it re-mints its serving cert in-process via a rotating
    GetCertificate source when less than 6h of validity remains; agent leafs
    were already kept fresh by the 12h renew sidecar once the broker stays
    reachable.
  • The agent's mTLS client leaf now hot-reloads on the live MCP/LLM path. Claude
    Code read CLAUDE_CODE_CLIENT_CERT/_KEY once at process start and cached the
    leaf for its whole lifetime, so after ~24h the boot cert expired and every
    gateway call failed with tls: certificate has expired — despite the renew
    sidecar rewriting a fresh leaf every 12h — until the manager was restarted. A
    new lever-agent gateway sidecar now runs a loopback (127.0.0.1) reverse-proxy
    that terminates plaintext from Claude and re-presents the always-current leaf
    to the broker over mTLS, re-reading agent.{crt,key} per TLS handshake. Claude
    no longer holds the rotating cert: boot points its MCP --transport http URLs
    and ANTHROPIC_BASE_URL at the loopback gateway. The proxy caps idle broker
    connections at 5m so a rotated leaf reaches the broker well before the 24h
    expiry, uses FlushInterval = -1 for MCP's SSE streaming, and binds loopback
    only (it holds the agent key). Boot-time discovery and llm-token calls still go
    direct to the broker (the gateway isn't up during pre-start).