Changelog#
What changed?
On this page
Changes to the hosted vault first, newest at the top, then the release notes of the mehen command line.
The platform#
28 October 2026: workspaces#
The vault moved to workspaces: one account belongs to any number of workspaces, each with its own members, teams, projects, credentials, machines and audit, at https://app.mehen.dev/w/<slug>/. The vault's data started fresh: machines enrolled before, credentials and projects did not carry over, and every machine enrols again in a named workspace with mehen enrol --workspace <slug>. The mehen release notes below say what changed for each version of the command line.
mehen CLI release notes#
The version is printed by mehen version --json, with the commit it was built from. Releases are served from https://mehen.dev/dl/; mehen update installs the latest one.
0.3.0#
Many workspaces on one machine without bending anything, an enrolment that cannot deadlock, and a CLI that checks for signed releases and installs them itself. Installed release builds with a compiled trusted release key check for signed updates automatically; a build with no trusted key updates nothing.
Getting there from 0.2.0. Run mehen update --json once on each machine. A 0.2.0 mehen cannot check a release signature, so this one update is checked against the manifest's hashes over TLS only; every update after it is signed, and a release build with a compiled trusted key checks for them automatically from then on. Do not run the installer on a machine that already has mehen. The first non-observational 0.3.0 command that uses slots migrates ~/.config/mehen in place (below); mehen workspace list is the local-only migration command; nothing needs re-enrolling, and credentials and projects are untouched.
Breaking changes#
- One state directory, one slot per workspace. Workspace identity state lives under
MEHEN_HOME(default~/.config/mehen); harness registrations and skill ownership remain per OS user, and update state is beside the executable:workspaces/<vault>/<tn_id>/for each active workspace andpending/<vault>/<slug>/while an enrolment waits. The 0.2.0 layout (the first enrolment in~/.config/mehenitself, the next ones incontexts/<slug>) is migrated by a slot-using command such asmehen workspace list, under a journal (migration.json) that resumes after a kill; device state is retained, while obsolete update-check files and the completed journal are removed. A directory that is not a slot (a hand-maderevoked-*, say) is named bymehen doctorand left alone. A managed host is never migrated automatically: its owner provisions the pinned slot first. MEHEN_CONFIG_DIRis retired to an alias. For 0.3.0 it is read asMEHEN_HOMEwhen it names the root, and refused asconfig_dir_retired(exit 2), before anything is read or sent, when it names a slot, a pending slot, an old context directory, or disagrees withMEHEN_HOME; the refusal names theMEHEN_HOMEto use. The alias goes in the next release: move launchers and MCP entries toMEHEN_HOME. On a managed host the pinned slot directory is still passed as before.mehen enrolprints the ask and exits 0. It no longer waits in the foreground or needs to be backgrounded: relay the link, thenmehen wait enr_...finishes the enrolment, from any shell.--waitkeeps the old behaviour. Runningenrolagain resumes the same attempt with the same key; on an active workspace it answersactiveand changes nothing.- Links stay alive in place. An attempt's link lives 60 minutes and
mehen waitrenews it while it waits (same id, address and fingerprint), up to 24 hours; there is no "issue a fresh link up to three times" any more. Only a terminalexpiredmeans a new attempt (mehen enrolagain). A 0.2.0 attempt migrated as pending is never renewed and expires at its old time. - No default workspace.
mehen workspace defaultis gone. A command that needs one workspace takes--workspace,MEHEN_WORKSPACEor the checkout'smehen.toml, else the only workspace there is; several and nothing deciding iscontext_ambiguous.status,doctor,ls,pendingandworkspace listcover every workspace. mehen status --jsonlists every workspace:{ home, allowed, workspaces, selected, next, cli }, each entryactive(with acheckfrom the vault that never deletes anything),pending,corruptorabsent.mehen status --workspace <slug> --jsonkeeps a scalar one-workspace result but renamescontexttoslot.mehen doctor --jsonis one report for the machine, each vault and each workspace, withcli,migrationandstrays.vault_statusandvault_doctorreturn the same shapes;vault_statushas noenrolledfield any more.- Status is observational.
mehen statusandmehen whoamineither migrate the root nor persist display-field changes;mehen workspace list --checkrecords a renamed workspace's new slug and name (only those, and only for the slot's own workspace and origin). On a legacy or resumable root they refuse with the next command to run;mehen workspace listperforms the local migration, then status can be retried.mehen doctorreports the journal without migrating it. - Listings span workspaces.
mehen lsandvault_listaddworkspaceto each item, and CLI tables add a workspace column when results span more than one workspace. A slug is unique only within its workspace: pass--workspaceor the MCPworkspaceargument on subsequent single-item operations. Aggregate pending lists also span the admitted workspaces; a singular selector limits the applicable commands. - One MCP server for every workspace.
mehen mcpserves every workspace the entry'sMEHEN_WORKSPACESallows, including one enrolled while it runs; every tool exceptvault_templatestakesworkspace(slug or id), needed only when more than one could serve. Each workspace is latched to the identity it first served; one whose enrolment changes under the server is refused until the agent restarts, while the others keep serving.mehen setup --workspaceand themehen-<slug>servers are gone: the nextmehen setupremoves the oldmehen-<slug>entries it wrote and reports them underremoved. mehen update --jsonanswers{ current, latest, available, updated, path, result, skills? }withresultone ofupdated,current,locked,heldorrolled_back, andskillsthe per-skill report instead of"refreshed".- The installer is for a fresh machine only. On a machine with a verified
mehenit printsAlready installed, downloads nothing and changes nothing unless--force(a human's decision, for example after losing the only release key). Its last line is nowmehen-install: {"v":1,"result":...,"version":...,"path":...,"onPath":...,"env":...,"rc":{...}}. It writes one guarded PATH line for zsh and bash and gives advice for other shells, and never overwrites a file it does not own. - A wider refusal when the user's home cannot be resolved. The managed-host marker is now read under the home directory the system's user database records for this uid (
getent passwd, then/etc/passwd, on Linux;dscacheutilon macOS), never underHOME. When that lookup fails or answers something unsafe (a relative path, a symlinked or group- or world-writable component, a home not owned by this uid), state-opening commands fail closed withkey_insecure(exit 6) instead of guessing (pure help, guide and version output does not open the managed marker), naming the uid and the fix. Only a definite "no such user" on a Linux host whose user database is the plain files is treated as unmanaged.HOMEandMEHEN_HOMEno longer move where the marker is read.
New#
MEHEN_WORKSPACES: a comma-separated list of slugs,tn_ids or<vault>/<slug>that this process may use. It only narrows; anything outside isworkspace_not_allowed(exit 2), and nothing inside a process widens it. An allowed workspace not joined yet shows asabsentwith the enrolment to run.mehen setupwrites onemehenentry per agent and carries the shell'sMEHEN_WORKSPACESinto it whenever it is restricted (andMEHEN_HOMEwhen not the default), so a restricted shell never writes an unrestricted server.--workspaces a,brequests a narrower entry and refuses a request wider than the calling shell; an existing wider entry is skipped until--reset-envrebuilds its environment.--mcp-file <path>writesmcpServers.meheninto any compatible JSON MCP client's file; use--agent vscodeor--agent opencodefor those clients' different formats.--print [--format json|toml]prints the entry and writes nothing.--reset-envrebuilds an entry'senv, for one that still carriesMEHEN_CONFIG_DIR. Amehenentry made by hand, or edited by hand after setup, isskippedwith its reason until--forcetakes it over, still under every env check. Each agent reportsmcpasadded,updated,unchanged,skippedorfailed, and each skill asadded,updated,unchanged,removed,externalorfailed; setup exits 1 on anyfailed.--remove --agent Xkeeps the skills while another entry remains.mehen enrol --connect <code>: a connect code from the workspace's Connect an agent page claims the attempt for that member, so it lands in their own Needs you and only they can accept it. A refused code isforbidden(exit 5).mehen workspace forget (<slug or id> | --slot <path>)retires one slot on this machine only, without the vault; the device stays on the Machines page until someone revokes it there.- Self-update. Releases are signed with a Secure Enclave release key whose public half is compiled into the binary. Installed release builds with a compiled trusted release key check for signed updates automatically; a build with no trusted key updates nothing. With a key,
mehenchecks for a release about every six hours in the background after a command and installs only a signed, newer version, with one journalled rename next to the binary.mehen updatedoes it now;--version <v>selects a named signed release, while an intentional downgrade needs--forceor a verified--rollback. Those local overrides still verify the release signature.MEHEN_NO_AUTO_UPDATE=1orautoUpdate: falseinsettings.jsonunderMEHEN_HOMEturns it off; CI and source runs never update themselves. After an update the command line uses the new version at once, and a runningmehen mcpsays in a tool result (and incli.mcpofvault_status) that the agent should restart to load it. cli_outdated(exit 1): the vault refuses amehenbelow its minimum version (HTTP 426) everywhere but freeze and self-revoke.mehenupdates itself before a command when it knows the minimum is above it, and a refusal in the middle of a command is shown once with the command to run again, never re-run. Every/v1answer carriesMehen-Cli: minimum=<v>(with; recommended=<v>when the vault sets one), and requests sendUser-Agent: mehen/<version>.mehen doctorgainshome,migration,on_pathandupdaterows, checks every workspace, and never migrates, writes or deletes anything.- The docs moved to https://docs.mehen.dev. The setup guide lives at https://docs.mehen.dev/setup.md and the skill at https://docs.mehen.dev/skill.md; the old addresses on
mehen.devredirect.mehen guideand the skill name the new address. mehen version --jsonaddsbuild(release,devor null when running from source) andhelper(embeddedornone);commitis also null for a source run without a build stamp. On macOS the Secure Enclave helper is embedded in the binary and extracted underMEHEN_HOME/binon first use; the separatemehen-sefile a 0.2.0 install left beside the binary is removed by the first update that 0.3.0 itself installs, when it and its checksum file match.
Fixed#
- Finishing the same enrolment from two processes (a backgrounded
enroland await) no longer revokes the device it just activated: the same key finishing twice is one success. Two different devices approved for one workspace on one machine isalready_enrolled, which now revokes and deletes nothing and says how to keep one. mehen revokeremoves a slot only when the vault revoked it or says it was revoked already, so "already enrolled, revoke first" and "revoked, enrol again" can no longer point at each other;mehen enrolon a revoked slot starts a fresh attempt.- Locks per slot stop two enrolments committing a key and a config that do not match.
- An approval granted just before a link's local expiry is no longer stranded: the record is kept until the vault says the attempt is over.
mehen updateno longer reports skills as refreshed when some were left alone.
0.2.0#
Workspaces. The vault now holds many workspaces, each with its own members, teams, projects, credentials, machines and audit, and every machine is enrolled in exactly one of them.
Enrolments made with 0.1.0 stop working at the cutover. The vault's data starts fresh: every machine enrolled by 0.1.0, every credential and every project is gone, and a 0.1.0 mehen cannot enrol against the new vault (its enrolment names no workspace). Update (mehen update --json, or curl -fsSL https://mehen.dev/install | sh), then enrol again with mehen enrol --workspace <slug>. The update keeps the old enrolment's files in ~/.config/mehen, so the new enrolment lands in ~/.config/mehen/contexts/<slug>: make it the default with mehen workspace default <slug> (which also decides what the plain mehen MCP server serves), verify with mehen status --json, and let the human delete the dead enrolment's config.json, device-key* and ledger.json from ~/.config/mehen; https://mehen.dev/setup.md has the steps. This is forward only: a 0.1.0 mehen cannot be used against the new vault, so there is no downgrade. The human re-adds credentials through intake links and re-creates projects with mehen init.
- Workspace enrolment.
mehen enrol --workspace <slug>is required; a baremehen enrolis a usage error (exit 2) and sends nothing. The slug is on the workspace's Connect an agent page. The machine's owner accepts it with their passkey;--team <team-slug>enrols a machine a team owns, which an owner or admin admits. The enrolment is verified against what was asked for before anything is written: an activation in another workspace, with another owner kind or without a complete scope writes no config: the CLI attempts to revoke the new device, reports whether that succeeded and, when it did not, names the device for a human to revoke. One enrolment per workspace per machine: the first lives in~/.config/mehen, the next in~/.config/mehen/contexts/<slug>, or whereverMEHEN_CONFIG_DIRpoints. Keys are scoped to one workspace and never reused across workspaces. - Contexts and selection. A context is picked before any signature:
MEHEN_CONFIG_DIR, then--workspace <slug or id>(a global flag) orMEHEN_WORKSPACE, then the checkout'smehen.toml(workspaceandvault), then the default. New commandmehen workspace list [--check] | show | default <slug or id> | bind. New local refusals, all exit 2 and never from the vault:workspace_unknown,workspace_mismatch(also for aMEHEN_VAULT_URLor key scope naming another origin or workspace),context_ambiguous,workspace_unbound.mehen statusandvault_statusname the workspace (id, slug, name), the owner and the context directory; every status call refreshes the recorded slug and name from the vault, never the id.mehen revokerevokes only the selected context. - Managed-host pins. A managed host's agent configuration must be current. An outdated one is refused as
pin_outdated(exit 2): selection fails closed, nothing is inferred, and only the harness owner can repair it. On a managed host only the pinnedMEHEN_CONFIG_DIRis accepted. - Private credentials and homes.
mehen requestandmehen connecttake--home private|project|workspace(--project <prj_id>for a project home, or the checkout's manifest project);vault_requestandvault_connecttakehomeandproject. The default isprivate: only the human who fills it in can use it, through their own machines with the private-credentials tick. The human may change the home on the page. - Overrides in
env ls. A member may set a personal override for a variable in an unprotected environment on the project page (their own private credential, or a plain non-secret value).mehen env lsmarks itoverride (your <slug>)oroverride (your plain value)in its text andoverride: truein--json;mehen env check --jsoncarries the same marker, while its human output only reports whether every variable resolves;mehen runnames overridden variables in its banner. No value is ever printed. - Project context for
fetch.mehen fetch --project <prj_id> --env <name>andvault_fetchcontext: { project, environment }name the project environment a credential is used for, which a protected credential requires, and so does a workspace credential that a team's or a guest's machine reaches through a shared project; a credential whose home is a project you may use, or your own private one, is reachable by name. A context is an extra requirement, not a default: the environment must reference a non-private credential, and a private one needs an authorised unprotected environment. It never changes the workspace. - Links under the workspace. Every ask link is
https://<vault>/w/<slug>/..., under the canonical slug of the workspace the machine is enrolled in. A link under another slug, an alias or another origin is refused and rebuilt from the id under the canonical slug, with a warning; a link cannot be trusted without a known slug. Asks carrycontext: { origin, workspace: { id, slug, name } | null, device }, the enrolment that raised them. - The identity gate. A workspace may require a verified Google identity on its domain. The vault then refuses a human who has not signed in with Google recently with
identity_required(exit 4): the human completes the sign-in in the workspace, and the agent repeats the step. Google only identifies; passkeys still authenticate and sign. Enrolled machines are not re-gated in this release; removing a member cuts their machines off at once. New codesaccount_pending,owner_inactiveandproject_unsharedare exit 4 as well. - MCP. One
mehen mcp [--workspace <slug or id>]server binds one context for its lifetime and refuses to serve anything else;vault_envin a checkout of another workspace isworkspace_mismatch("start a second server").mehen setup --workspace <slug or id>registers that workspace's server by its stable id, asmehenfor the default context ormehen-<slug>for another, without touching other registrations;mehen setupwith no flags writes exactly what 0.1.0 wrote. Tool names are unchanged.mehen pendingandvault_pendingshow this machine's own asks and its own "machine added" notice only. - Policy proposals (
mehen policy preview|propose,vault_policy) keep their surface but are refused by the vault in this release (forbidden, "Policy widening is not available yet");show,cancelandstopwork. - Help and next steps from the CLI itself name
--workspace <slug>wherever they tell you to enrol (the vault's ownnextfor an unknown or revoked machine still saysmehen enrol; add the workspace), and use neutral example names.
0.1.0#
First public release: mehen.dev/install, mehen setup, mehen update, the ask block, enrolment with a kept key, mehen mcp, mehen run, templates, mehen check and mehen pending.