Cotal
DocsAPI Reference
Mesh lifecycle

CLI shutdown

down stops a stack or saved owned services. clean removes selected retained state. reset previews and applies guarded cleanup of one stopped local store or space identity.

down

Every down runs to its end when the reader of its output goes away, as | head does. It keeps stopping, writes nothing more, and exits with the stop's own status.

down <service-name> routes one saved owned definition through the same selection, confirmation, reservation, native manager-impact and stop controller as service stop. Use --instance <full-id> when selection needs a full instance pin. --yes, --dry-run, --space, --json and --plain keep that controller's meanings; structured receipts name down while retaining the service-stop result and actual identity. Unknown definitions do not fall back to legacy component pidfiles. A controller stack names its members stack-broker, stack-auth, stack-delivery and stack-manager. A preset name reaches the member built on that preset, as in service, so cotal down manager stops stack-manager and cotal down delivery stops stack-delivery through the service stop, and the receipt's target names the member and the name you gave as requested. Several names in one down resolve each the same way, so cotal down manager delivery stops stack-manager and stack-delivery, and each member of the target carries its own requested. A name that is no preset, such as nats, refuses TARGET_NOT_FOUND there and names the stack- member to use, and a name with no member names cotal service, which lists the saved definitions. Among several names such a name refuses the whole set with the same next step, so no service stops. A set never stops the broker, so nats, broker and stack-broker each refuse the whole set TARGET_NOT_FOUND before any member resolves, with the same message for each name. The next step is a bare cotal down, which stops the broker with the whole stack. A saved service of your own named nats or broker is that service. A set that names another saved service running a broker refuses UNSUPPORTED and names that service's own stop. A stop of stack-broker refuses LIFECYCLE_BLOCKED while a legacy daemon record under the root may front a process, or another service instance of the space is live or unresolved. For those instances it names bare cotal down, or cotal service stop <name> --instance <id> --space <space> --yes for each one. When one of them is not a member of the stack, such as one added with cotal service add, bare cotal down refuses too, so the refusal names that service's stop first, then bare cotal down, or the stops of the stack's own members and the broker stop again. Its dry run warns with that refusal. Completion offers saved definitions without imports or a network request. No hidden connector repair or shared catalog preparation precedes this route.

Multiple names resolve as one selected-space set. All owned targets and process roles are checked before effects. After confirmation, the controller reserves every definition in stable order and rechecks all records and native impact before the first stop. Stops follow the displayed input order, with each native guard rechecking at effect. A later failure stops the sequence; one receipt retains completed, failed/unknown and unattempted members. Reservation/preflight refusals also retain the full selected set. Release is attempted for every acquired definition hold; a reported release error makes an otherwise known outcome partial without erasing known stop results. An already unknown stop stays unknown. Lock cleanup is not independently proved. This is not atomic and no rollback or cleanup is claimed. Dry-run reads metadata without reservations, provider imports or native impact queries. Duplicate names and --instance with multiple names are usage errors. Named --with-agents refuses and points to cotal service stop <manager-name> --instance <full-id> --with-agents; it is never dropped.

Stack preview

Bare down --dry-run reads local process records for one registered space at the current project root. A root with no .cotal/ directory, or with no stack registered at it, refuses TARGET_NOT_FOUND, exit 2, because there is no stack there to preview. Its next step is the folder where cotal up started a stack, or, when this machine has no stack started or registered at all, cotal up in a project, as cotal status says. A bare down in such a folder refuses the same way. A root whose stack is already stopped shows the no-op described below instead. In the machine home, a stack an earlier release started there is read the same way, and the Next names cotal down --preserve-state, its stop, or says that none of it is running. Add --space <name> to confirm that exact space belongs to this root; --json and --plain produce a partial, finite inspection receipt. It lists pidfile liveness, readable additive deployment IDs, retained additive attempts and unreadable paths under this root, and saved service definitions, instance IDs, recorded states and hosts, supervisor receipts, and retained install, uninstall and unit-action attempts in the selected space's workstation store. Saved records are not attributed to this project root, and their recorded states do not prove live process or supervisor status. A submitted native attempt may have changed state; the preview neither reconciles nor retries it. For each saved service instance it also shows whether it blocks bare down: one that still runs or cannot be verified blocks it, with the reason, and one already stopped is skipped. When one blocks, the preview warns with the refusal the real stop would give. That check observes processes on this host and changes nothing. Other local registrations with the exact recorded broker address appear as possible sharing. No match does not exclude address aliases, remote tenants or live attachments. The preview does not establish process ownership, the complete service set, exclusive broker use or agent impact. --with-agents only marks the requested policy. It does not reserve or signal a process, and the receipt is not an authorized stop plan. A fresh team show summary points to this read-only command from the recorded root.

On a root with a controller journal, bare down reads only its recorded full instance IDs. On an interactive terminal it shows the member set and the live manager's native agent impact and asks to confirm, then stops manager, delivery and broker in that order. --yes acknowledges that scope in scripts and prints no plan. The receipt names the mesh once, lists each member with its result in stop order, and names cotal up as the next step. --with-agents requests the manager's native retirement policy. Another live service instance in the space refuses LIFECYCLE_BLOCKED before the first stop; the Next names the cotal svc stop <name> --instance <id> --space <space> --yes command for each one. So does a process record under the root that may front a process, such as a daemon an earlier release started; the message and the Next name each record's path and pid. A record whose process is gone, or whose pid now runs another start than its identity pin records because the pid was reused, fronts nothing. It refuses neither bare down nor a stop of stack-broker, down --dry-run names it, and a complete bare down removes it with its pin and names it in a STALE_PROCESS_RECORD warning. The process that holds a reused pid is never signalled. The broker scope is checked again after its dependants stop. A failure stops the sequence and preserves completed and unattempted members. A member stopped by signal that has not exited 15 seconds after SIGTERM gets SIGKILL. Its row then reads stopped with SIGKILL, and a STOP_ESCALATED warning names its pid. Only a complete confirmed stop removes the delivery credential and local registration. After the delivery daemon stops, down reads its lease. When the daemon was already gone, or the lease still names the daemon that credential gave, as after a kill or a release a full store refused, the credential stays, and the next up uses it to recognise the row and wait it out before it mints a new one. When the lease cannot be read, the credential stays too, and a DELIVERY_LEASE_UNREAD warning says so. The JSON cleanup field then reads registration-removed-delivery-credential-kept. Saved definitions and the journal remain for the next up, and the space stays prepared at its root. A selection that names it stays, so the next command run without --space refuses with TARGET_UNAVAILABLE, exit 2, and names the root to start it in, rather than reach another space. No descendant, lease or authority cleanup is implied. A dry run reads records and, with a live manager, how much the space's store holds. It takes no lock, does not establish native impact and exits 0; its JSON marks completeness as partial. It lists members in stop order, says when the stop would first settle an earlier operation, and names a scope check the real stop would refuse as a warning. A member whose recorded process is already gone is recorded as stopped without loading its adapter or credential, so a second down --yes stops nothing and is a no-op. Before it stops a live manager, down reads the workflow runs it hosts and records the unfinished ones, with the steps they wait on, in the stack journal, where reset store lists them. A read that fails is recorded with its reason and does not stop the down; the JSON receipt carries the record as operation.runs. When the manager is live but the broker is not, down cannot read the manager's agent impact and refuses before any stop, from a managed seat's shell too. Run cotal up --space <space> to restart the broker from its recorded inputs while the manager keeps running, then run cotal down. On Linux, a broker process stopped by a signal, such as SIGSTOP, is read from its process state, and down refuses TARGET_UNAVAILABLE, exit 2, at once, naming its pid, and stops nothing. When the broker's process runs but does not answer, the manager read waits out its bound and down refuses the same way. The broker stops last and is never force killed, so down does not signal it. Resume a suspended broker with kill -CONT <pid> and run cotal down again; end one that stays unresponsive, then take the route above. The manager's stop rides a control credential whose issuance record the broker stores first, and a store over its cap refuses that write. With a live manager, down then refuses DEPENDENCY_UNAVAILABLE, exit 2, before any stop, naming the store and the --max-file-store cap this machine recorded. Make room with cotal history clear --yes, then run cotal down. That purges all of the space's channel history. To keep it, end the manager on the stack's host with kill -TERM <pid>, using the pid the refusal names. A plain SIGTERM stops every agent that manager runs by its default policy, as supervise describes, and it needs no credential. Then cotal down stops the other members, and cotal up --max-file-store <bytes> with a larger cap starts the stack again with its history. The signal reaches only a process on that host. cotal service stop stack-manager and restart refuse the same way before submitting a stop, and name cotal history clear --space <space> --yes and then the same command again. A stop also names kill -TERM <pid> as the way to keep that history. A dry run names that refusal as a warning when the space holds more than the recorded cap.

nats-server disables JetStream when its store refuses a write for permission or for space, and only a restart brings it back. While the broker's log on this machine shows that, the manager's stop cannot record its credential either, so down, service stop stack-manager and restart refuse DEPENDENCY_UNAVAILABLE, exit 2, before any stop, and name the logged failure. up refuses the same way, because it cannot check the space's streams on that broker. A dry run names the refusal as a warning. Each names the way out: end the manager on the stack's host with kill -TERM <pid>, which stops every agent it runs by its default policy, then run cotal down, fix the logged cause, and run cotal up. Fixing the cause alone does not bring JetStream back.

Controller roots refuse preservation and manifest flags rather than switching to the legacy launcher. Outside a terminal, a bare down that would stop a running member refuses without --yes, exit 1, and prints that line and its remedy without the command help. A stop records each step in the stack journal in the root's .cotal. When this account cannot write that folder, down refuses PERMISSION_DENIED, exit 2, before the plan, names the folder and the chmod u+w that fixes it, and stops nothing. Each member's stop is also recorded in the space's service instance records, under services/ in the Cotal home, and a folder there this account cannot write refuses the same way. A dry run names either as a refusal the real stop would hit.

A controller stack's members are stopped through the registration cotal up wrote for it. When an older meshes rm --force dropped it, or an older meshes add --force replaced it with one added by hand at the stack's own root and broker, down rebuilds it from the stack journal for the stop and says so with a REGISTRATION_REBUILT warning, as --dry-run does. It does that only while the stack's broker is the launch the journal records at that root and address, and otherwise refuses CONFLICT, exit 2, before any member stops. A registration added by hand at another address or folder is not replaced: down refuses CONFLICT and names cotal space forget <space>, after which down stops the stack through its journal. While another registered space names this stack's exact broker address, bare down refuses LIFECYCLE_BLOCKED, exit 2, and stops nothing, because one listener answers for both registrations. It names each of those spaces with the cotal space show <space> that reads one, the cotal space forget <space> that takes it off this address once its own stack is stopped, and the cotal down to run again after that. A user-auth stack's registration is not rebuilt, so it refuses UNSUPPORTED. A stop that refuses or is cancelled before any member stops puts the registration back as it was.

A bare down stops only the stack of the folder it runs in. In a folder whose root holds no stack, when the space named by --space, or else the selected space, is a stack this machine started in another folder, down refuses with TARGET_NOT_FOUND, exit 2, and stops nothing. A folder whose own .cotal holds no stack, such as one a failed start left, answers for itself unless --space names another folder's stack. In a folder with its own controller stack, a --space that names another space refuses with CONFLICT, exit 2, and names this folder's stack. Each refusal names the space and, when this machine started it elsewhere, its root, and its next step runs the same down from that root. --json and --plain carry that refusal as structured output, because the folder has no stack for a bare stop to print its own lines about.

When the folder of a running controller stack is deleted, its stack journal and broker configuration go with it, so no member's own stop can run. A running broker writes .cotal/nats back into the folder, which does not bring them back. down --space <space> from any folder stops that stack from the instance records this machine keeps, and so does a bare down in the folder the broker wrote back, or in a folder with no .cotal of its own while the space is selected. Each running member gets SIGTERM at its recorded process, manager first and broker last, after down checks that the process is still the one its record pinned, and its record is marked stopped. The manager stops its agents by its default policy, so --with-agents and the other stop flags are refused. A member that does not exit within its window, 90 seconds for the manager and 30 for the others, ends the stop with OPERATION_UNKNOWN and later members are not stopped. Once every member is stopped, down removes the stack's saved definitions, their instance records and the registration that names the folder, so the space can be created and started in another folder. The same stop runs for a folder moved or renamed while its stack ran, which took its journal, store and trust with it. Its Next names cotal space create <space> --adopt --root <folder> first, which adopts those records where the folder is now and keeps its history, then cotal space create <space> for another folder. Outside a terminal it needs --yes, and --dry-run lists each member with its pid and process. cotal status marks such a project folder as gone, with selected.rootGone set to true in its JSON, and its Next names cotal down --space <space> --yes. A command that connects to a static-auth space, such as send or history, is refused with DEPENDENCY_UNAVAILABLE, because the seed went with the folder, and its Next names the same down.

Already-stopped stacks

A bare down in a folder whose stack is already stopped stops nothing. It reports that the stack in that folder is not running and names cotal up, outcome no-op, exit 0, and writes nothing, so it needs no --yes. --dry-run, --json and --plain show the same. A controller stack counts as stopped when its last whole-stack operation was a completed down and no member and no other service instance of its space runs. That check reads no process record under the root; those are read only as described below. A stack that stopped any other way takes the full stop, which also finishes its cleanup. A legacy root counts as stopped when no registration names it, or only the one a space named after its folder keeps, and no *.pid record remains at the top of its .cotal, and the root still holds the broker configuration a start rendered there or a space prepared there. Without that evidence a legacy root still refuses, with one next step: TARGET_NOT_FOUND when nothing shows a stack there, and CONFLICT when several registrations name the root or a process record remains that no registration explains. Before either kind of root reports the no-op, down reads every record a local-process descriptor names under the root, an installed extension's included, at any path and name under .cotal. A descriptor whose record name holds a space is read for every space its files under the root name, as well as the stack's own. A record whose process runs, whose liveness the kernel does not answer, or that cannot be read or attributed refuses LIFECYCLE_BLOCKED, and so does a file whose space part decodes to no space. Nothing outside the root's .cotal is read: a link anywhere on a record's path refuses at the link, unread, as does a record that is not a regular file, is larger than a process record, cannot be opened, or changes while it is read. Each is named once, at the path where reading stopped. A .cotal that is itself a link never gives the no-op: on a stopped controller stack down refuses at .cotal, and on a legacy root it refuses CONFLICT before any record is read. A template whose {space} normalizes away names one path for every space and is read as that path. The refusal lists each record by name, path under the root and pid, and its next step names the pids and paths to check. It stops, adopts and removes nothing. --with-agents on a stopped stack refuses LIFECYCLE_BLOCKED, because no running manager can stop the agents; start the stack first. Its preview, cotal down --dry-run --with-agents, shows the no-op and warns with that refusal. When the stack's last completed down took --with-agents, or the root never started a stack, no agent of it can remain, and --with-agents reports the same no-op.

Legacy stacks

The legacy bare-stack and manifest apply paths described below are retained. They do not accept the new named-service JSON/plain/yes apply receipt flags, except that --json and --plain show the no-op of a stack that is already stopped. A bare stop of such a root asks for no confirmation, and --yes there changes nothing, so cotal down --yes stops a stack on any root. Before a retained bare apply reaches its pidfile sweep, it now requires one locally started registration under a real isolated project root and refuses pending maintenance transitions, any valid or unreadable additive ledger or attempt under that root, or any other local registration at the exact recorded broker address. The additive ledger refusal names each ledger with the command that finishes it: cotal down --run <id> for a readable one, and for an unreadable one the reason it could not be read and the same command once it is repaired. It reads each record its sweep stops as the stopped-stack check above does. A record that check leaves unread, such as one behind a link, refuses LIFECYCLE_BLOCKED before the first stop and is named once, where reading stopped. No process behind it is read or signalled, and no record is removed. When a manager or delivery daemon has no pidfile but its lease row is still there, down prints <daemon> has no pidfile; waiting up to <n>s to see whether its lease is renewed and watches the row until the lease TTL ends. A row that is written again refuses LIFECYCLE_BLOCKED, exit 2, before the broker stops. The refusal names that daemon and says to stop it, or put its pidfile back, then run cotal down again. A row that expires, or that nothing writes until the TTL and a five-second grace have passed, was left by a daemon that exited without releasing it, such as one whose broker stalled past its broker-gone window, and down goes on. A delivery daemon gets two seconds to release shard 0 of its lease after the signal and then exits anyway. A signal that lands while its connection redials first waits up to five seconds from the drop for the redial. So after it stops, the bare stop reads the lease while the broker still runs. When the row still names the credential this folder gave that daemon, or the lease cannot be read, the credential stays and down prints a ! line saying so. The next up uses it to wait the row out, as after a crash, and then starts the new daemon. These are known-blocker checks, not a complete live broker-sharing or stop plan; aliases, remote tenants, agent impact and process identity remain unproved. These paths do not check full whole-stack target ownership, set exclusion, confirmed agent impact or schema reconciliation. The retained down --preserve-state route separately refuses a retained or unreadable additive record under its maintenance lock, without adopting its effects into the preserved set. A validated completed restore in active for the registered space does not block ordinary bare down. The other root, ownership, sharing and native process guards still apply, and stopping leaves the restore journal and fallback store unchanged. The machine home is not an isolated project root, so bare down refuses the home stack. Stop it with cotal down --preserve-state, which keeps its state for the next cotal up. The refusal names that command. --preserve-state takes its agent inventory from the manager that serves the space. When no manager holds the space's lease, as on a stack started with --no-manager, it refuses TARGET_UNAVAILABLE, exit 2, before it stops anything or writes a maintenance record. Start the manager with cotal up, then run cotal down --preserve-state again. The cut stops the delivery daemon before it takes the manager's inventory, so no durable join or leave can race it. A refusal after that point (the manager refuses to prepare, an endpoint the manager does not manage is live, or a seat cannot be checkpointed) starts the daemon again and leaves the mesh running and unfenced; a live unmanaged endpoint refuses LIFECYCLE_BLOCKED, exit 2, and names the endpoints. When the daemon cannot be started again, the refusal says so and names cotal up. An open mesh runs no delivery daemon, so a refusal there starts none, also when the cut stopped one that an earlier up left running. It also refuses LIFECYCLE_BLOCKED, exit 2, before it stops anything, while a saved service in the space still runs or cannot be verified, because the service depends on the broker it would stop. The refusal names each one and the cotal service stop <name> --instance <id> --space <space> --yes that stops it. Bare down refuses the same way. The preserved state records its store by absolute path. If the project folder is moved after down --preserve-state, up, backup create and backup restore refuse CONFLICT, exit 2, name the folder it was moved from, and say to move it back there first. A store missing from its recorded path is refused TARGET_NOT_FOUND with that path; only backup restore --accept-missing-source restores over a lost store.

After those checks, a retained bare apply refuses LIFECYCLE_BLOCKED, exit 2, before its pidfile sweep while a service instance in the selected space still runs or cannot be verified, because the service depends on the broker the sweep stops. It stops nothing. As on a controller stack, bare down does not stop a service itself: the refusal names each instance with the cotal service stop <name> --instance <id> --space <space> --yes that stops it, then cotal down again. An instance whose process is already gone is skipped, and saved definitions stay. An unreadable service record keeps the code of its read refusal. A retained native attempt, an unsettled request, a process that cannot be observed, or a record from another host, which bare down does not touch, refuses too, and the remedy names the command that inspects or stops it.

cotal down
cotal down --with-agents
cotal down --abandon-stop <request-id> --yes
cotal down --abandon-launch <instance-id> --yes
cotal down --preserve-state [--store-dir <dir>] [--session-store <dir> …]
cotal down --dry-run [--space <name>] [--json|--plain]
cotal down <service-name> [<service-name> …] [--instance <id>] [--space <s>] [--yes|--dry-run] [--json|--plain]
cotal down -f <cotal.yaml> | --run <id> [--dry-run]
FlagDefaultMeaning
--file <cotal.yaml>, -fnoneTear down this manifest's deploy
--run <id>noneTear down one additive ledger, written by the retired spawn -f deploy, by its run id
--space <name>current spaceWith service names: the space whose saved definitions to stop. With bare --dry-run: confirm that space belongs to this root. A bare stop refuses it
--dry-runoffPrint the manifest teardown, the named service stop or the bare stack preview, mutate nothing
--with-agentsoffBare whole stack only: also stop and deprovision every managed agent
--abandon-stop <request-id>noneController stack, with --yes: abandon a member's stop that no receipt settles, then stop the stack. See the settlement rules under up
--abandon-launch <instance-id>noneController stack, with --yes: record failed a member launch whose record names no process, then stop the stack. See the settlement rules under up
--preserve-stateoffBare whole stack only: fence the manager, retain principals and durable state, stop and prove the stack down, then publish ready
--store-dir <dir>the recorded store, else .cotal/natsWith --preserve-state: the store to cut. The mesh record names the store the broker runs on, such as a backup restore --store-dir target, and a different path refuses CONFLICT before anything stops
--session-store <dir>noneWith --preserve-state: a harness transcript store directory to capture with every continuation-capable retained seat. Repeatable. No default and never inferred from a connector name; a path that does not exist or is not a directory is refused before anything stops

For an additive ledger from the retired spawn -f, down -f keeps an authenticated channel when membership cannot be read. Its output includes the read failure, rather than calling every failure an absent feed. A user-auth teardown uses the signed-in user's control authority and an admin read view, plus the channel-writer view for deletion. Run it from the registered checkout as the owner who deployed the agents, with the admin scope needed to inspect membership and remove channels. It does not use the local static signer to stand in for that user's identity. If the issuer predates the membership snapshot read, upgrade it and obtain a new credential before retrying the retained run. See membership snapshot reads.

Agent policy

Bare cotal down stops the whole local stack in dependency order and leaves managed agents running when their runtime lets them outlive the manager. Before signalling the manager it verifies the spare capability of the exact recorded manager, which records what that manager's stop does with its seats, and it reports the agents left behind plus cotal down --with-agents as the explicit reap. When the manager had no managed agents, it prints no report. When they cannot be listed, for example on a user-auth mesh with no login on this machine, it prints could not list managed agents (<reason>) and the stop continues. The built-in pty runtime keeps each PTY inside the manager process, so those seats cannot outlive it: every manager stop stops and deprovisions them, and down reports them as stopped. A manager retires at most 8 seats and chat sessions at a time, and the rest wait their turn. Every manager stop the CLI makes runs this one path: down, Ctrl-C on a foreground cotal up, the teardown after that up's broker exits, the leftover-manager stop before cotal up -f, and the delivery cutover. Each holds the manager's stop reservation, so a second stop while one is in flight is refused, names the process holding it, and leaves that stop's --with-agents policy in place. On Windows, manager and delivery stops send an authenticated local cleanup request because Windows process signals terminate without running cleanup handlers. The request requires the process's private capability, its creation identity and the live stopper's identity and reservation. Other platforms use SIGTERM. A cotal supervise that receives SIGTERM or SIGINT while it is still starting lets the start settle, then runs the same stop and exits without printing manager up, so its liveness lease is released and an immediate restart is not refused. When that start fails, the stop still releases the lease before the process prints the start error and exits 1. A process still running after 20s on the Windows cooperative path, or 15s after SIGTERM, receives SIGKILL. Ctrl-C stops the manager first; when that stop is refused or the manager's exit cannot be confirmed, Ctrl-C signals nothing else, prints the refusal with the reap route, and leaves the stack running; end it with cotal down --with-agents. --with-agents is a one-shot destructive policy bound to the exact verified manager process and the exact live down stop reservation; a stale, malformed, crashed, or different stop attempt cannot turn a later bare shutdown destructive. If a managed agent cannot be proven stopped within the manager's stop timeout, the manager logs which one, still closes its broker connections and console listener, and exits with code 1. It does not release its pidfile, liveness lease or service registration in that case, so no successor is handed authority while that agent may still run; the lease lapses on its TTL. A named down is the service stop above and never stops a legacy component by its pidfile, so cotal down manager or cotal down web on a legacy stack is refused unless a saved definition has that name. Only bare cotal down stops a legacy root's components, all together, under the folder you run it in. It takes no --space. Its stop lines follow a Mesh: <space> heading, and its Next names cotal up to start the stack again. A component pidfile that holds content that is not a pid names no process, so down says so, keeps the file, does not stop the broker, and keeps the artifacts and the registration. Its Next first asks you to confirm that no such component still runs for the folder, because cotal cannot check a record that names no process and a manager that does not answer on the mesh may still be running. It then names rm <pidfile> and the same cotal down. A bare down or down --dry-run whose --space names a space whose stack is recorded in another folder refuses TARGET_NOT_FOUND, exit 2, naming that folder and the stop or preview to run there. The -f / --run forms tear down a manifest deploy without stopping the whole mesh and cannot be combined with service names or --space.

A pinned manager with no spare-capability record is not signalled by bare cotal down. A current manager always publishes the record, so a missing one means an older manager: one that predates capability reporting, or one whose pty runtime reported that it cannot detach its agents. Stop each managed agent explicitly, then run cotal down --with-agents from the mesh root to stop the whole stack. An older manager does not understand the reap request, which is why the agents must already be stopped.

After the known-blocker admission checks above, the retained bare cotal down inventories by pidfile. When this folder's registered broker answers and no nats.pid records it, the command does not say nothing is running. It names the space and the broker address, says no pidfile records that process, says it will not stop a process it did not start, and exits 1. Stop that broker with whatever started it (an init unit, a container, or the hand-run process). cotal meshes rm <space> only drops the registration. The probe runs whether or not other owned components were running: they stop and clear their artifacts first, then the broker is named. A --dry-run does not run this probe.

down reads each process record once. A component that exits and removes its own record while down runs counts as having no record, so the stop goes on. Any other failed read is an error. An extension-removal reservation in a component's pidfile refuses the stop: it reports the removal in progress while the owner is live, calls the reservation stale only when the owner is proven gone or is not a pid, and otherwise refuses without advising deletion because the owner's liveness could not be confirmed. Whole-stack shutdown keeps delivery credentials until every required component has stopped. A refused stop preserves both the space-scoped credential and its legacy copy. Credential removal errors remain failures. down removes the manager's delivery-aware marker, shutdown intent and spare capability only after every component has stopped, so a refused stop keeps them too.

Process identity

Teardown verifies pinned process identity before signalling. PIDs are recycled by every OS, so a recorded pid alone is not a durable target identity. up and the dashboard's native child record each process's creation identity in a sibling <pidfile>.identity pin, which holds the pid and the process start reported by the OS. Every stop path, including down for the broker, web and extension components, and the manager, delivery and auth-service stops, applies the same rule. A pin that names a different start means the pid was reused, so teardown refuses and preserves it. A controller stack's bare down signals no such record, so it removes it, as described above. A torn or unreadable pin also refuses. On macOS, the reader calls sysctl through a bundled Node-API binding for x64 or arm64 and records the process start in microseconds. A macOS pin that an earlier release wrote holds the ps -o lstart start date instead and is verified with that same reader. That date keeps its spaces, including the padded day, in both the identity pin and the one-shot shutdown intent. A single record cannot span lines. On Windows, the reader calls GetProcessTimes through a bundled Node-API binding for x64 or arm64. It requests limited process-query access and preserves the creation FILETIME as a decimal string. It launches no subprocess. Access denial leaves identity unverified; an unreadable pin still refuses teardown. On either platform, a missing or damaged binding is an installation error and has no fallback.

The pidfile and its pin are published by renames, and the pidfile rename is the commit point. Just before it, the pin holds two lines: the old process's and the new one's. A launcher that dies mid-publish therefore leaves the old record or the new one, each checked against its own pin line, never a pidfile without its pin. An old record with no pin is legacy, so its line holds - in place of the token and it stays legacy until the commit. A CLI older than this change reads a two-line pin as torn and refuses.

Publishes of one pidfile are serialized by a lock file beside it, <pidfile>.publish.lock, because the launcher and the daemon it starts both publish the same record. The next publisher reclaims a lock left by a crashed one. When no start token can be read for the new process, its pin line holds - in place of the token, which reads as a legacy record, and the publish ends in the legacy shape: a pidfile with no pin. Teardown, and a daemon removing its own record on exit, take the same lock and remove the record only while the pidfile still names the pid they stopped, so a stop that races a publish leaves the new record whole. A directory at the pin path is refused by name and the record is kept, because no publish writes one and removing it could delete files Cotal did not write.

The web dashboard service claims web.pid with an exclusive create, so a second dashboard for the same mesh is refused, and writes its pin right after the claim. A stop that runs between the two reads a legacy record. Any existing web.pid refuses the start, whether or not its process still runs, and the start neither adopts nor reclaims it.

The pidfile pid and the pin pid are two coordinates. Automatic cleanup follows proven death of the pidfile target (ESRCH on that pid): a torn or unreadable sibling pin does not wedge a dead pidfile pid. A torn pairing where the pin names another pid, while the pidfile pid is still live or not proven dead, still refuses. Inspect both pids with ps. Do not delete <pidfile>.identity to force a stop; that weakens target-identity protection. Once the pidfile process is dead, rerunning teardown clears the stale record automatically.

A Windows manager or delivery daemon without the cooperative capability must be stopped through its original supervisor before restarting with this version. Teardown refuses it without signalling or deleting its live record. It does not wait for a lease to expire or delete a lease by local pid.

For components using signals, the first teardown after upgrading a running pre-pin stack has a narrower guarantee. A live record with no identity pin is signalled after a loud warning that it predates identity pinning. Restarting the component writes the pin, so later teardowns receive full match and mismatch protection. The same warning applies on platforms where no stable start token is available. For a legacy manager, bare cotal down also warns that agent sparing cannot be verified before it signals. Because the CLI cannot establish which SIGTERM handler that already-running binary carries, it never presents the pre-signal seat inventory as confirmed spared; a genuinely older destructive handler may still reap those agents. --with-agents publishes a one-shot reduced-guarantee handoff bound to the recorded manager pid and the live .stopping reservation's inode, then signals unconditionally. That handoff cannot be replayed by a later stop attempt. A pin that exists and does not match the live process still refuses before signal.

--with-agents performs the old destructive logical teardown: managed processes stop and their credentials, ACL rows, and delivery footprints are deprovisioned. --preserve-state is a different maintenance transition: it stops retained processes while suppressing leave/deprovision cleanup, persists the manager's same-principal resume inventory, stops the entire stack without removing run/auth artifacts, and publishes a stable inode-bound cut only after every recorded process is proven stopped and the exact recorded NATS endpoint is unreachable. A missing or stale broker pidfile never counts as stopped. The attempt is bound durably before the manager is fenced, the resume document and attempt-bound cut-intent are fsynced before manager commit, and the manager's commitment itself is journaled (cut-committed) before any process stops. A retry after a crash at any of those boundaries reuses the exact recorded attempt and finishes the remaining stop and endpoint proofs idempotently, without needing the (by then intentionally dead) manager. A partial cut never publishes ready. It cannot be combined with component names, manifest teardown, or --dry-run.

On a mesh that runs the delivery daemon, the cut stops it first so no join or leave can change membership under the manager's inventory. Before any manager-owned endpoint stops, the cut then reads the presence roster and refuses while an endpoint the manager does not own is live. A refusal leaves the delivery daemon stopped. A deleted presence key is the only row it reads as absent. Any other row that is not a card with an id equal to the presence key it was read under and a string name refuses the cut with presence record <subject> is malformed. A participant can write any value under its own key, so the key is the identity the cut trusts.

Seat checkpoints

Seat checkpoints. After the stack is proven down, the cut writes one checkpoint per retained seat under .cotal/maintenance/v1/checkpoints/<attempt>/<seat>/, and prints the path, the continuity class and the generation for each. The path carries the preservation attempt because a checkpoint is immutable once sealed: a shared directory would make the second cut in a root refuse on the first cut's leftovers, and clearing it would destroy an artifact a rollback still needs. The capture happens only at that point because anything earlier races a harness that is still writing its transcript and its working tree.

Each checkpoint directory is created 0700, refuses a destination that already exists, and holds:

  • repo.bundle, the seat cwd's reachable history, anchored on the base commit the record names by full object id;
  • repo.index.diff and repo.worktree.diff, the staging state as two diffs, base to index and index to worktree. Two rather than one because a single combined diff restores a mixed tree with the right bytes and the wrong index: a source reporting MM README would come back as M README;
  • repo.untracked.tar, the untracked files in scope;
  • the harness session pointer, when the seat's connector declares one, and the transcript store files the operator named with --session-store. Each records where the destination puts it back as an anchor (the workspace root, the account home, or the seat's cwd) plus a relative path, because the destination's root and home are its own and the source host's absolute spelling would either miss them or write outside them;
  • checkpoint.json, written last, after every digest is computed over the bytes that landed.

The record carries the manager's resume entry unchanged as its first field, then the space, the seat name, the recovered lifecycleUid, the writer generation the cut was taken at, capturedAt, the recency horizon, the applied profile revision, the seat's git status --porcelain as the cut read it, and the continuity class. Every captured file is listed with its byte size and sha256, so an operator verifies the whole artifact with sha256sum and git bundle verify. No secret values, no operator keys and no source-host launch material enter it.

The generation is the highest one this root has claimed for the seat in .cotal/auth. A generation entry there that is a symlink, a directory or anything else that is not a regular file refuses the cut, as every other auth record does.

The continuity class is what the connector declares, capped by what the checkpoint carries. A connector declaring session continuation classifies as exact, but reopening a session takes both halves, the pointer that names it and the store that holds its transcript. A checkpoint missing either one cannot reopen that session, so it is recorded as fresh when the connector declares a fresh start and drain-only otherwise. A pointer with no store is capped the same way as a cut carrying neither, because it names a session whose bytes the artifact does not contain. A class is a promise the destination is entitled to act on, so it never describes bytes the artifact does not contain. The transcript store stays an operator input: this repository does not know where a harness keeps its transcript, so exact requires --session-store to name one.

The recorded status is read under the same selection rule as the untracked set, so it describes the state the captured bytes can reproduce. The destination re-reads it in the promoted tree and refuses a difference.

The untracked selection rule is recorded in the record and is git ls-files --others --exclude-standard -z, excluding .cotal/. It honors .gitignore, so an ignored file the seat needs does not travel and has to be moved separately. The .cotal/ exclusion is a secrecy boundary rather than a size one: when a seat's cwd is also the mesh root, the control directory is untracked, and without the exclusion the broker trust material, the space account, the manager instance identity's private seed and the seat's own credentials would land inside the artifact. A checkpoint carries credential references only; the destination resolves that material itself.

A seat whose launch options could not be resolved is refused rather than checkpointed, with the manager's own wording: imperative launch options have no non-secret durable source (<keys>). The refusal arrives at prepare time, so the cut stops before any child does.

A delegated seat (SPEC §13.17) is refused at prepare time too, with a delegated seat is not resumed by a later manager; stop it before preserving. A manager stop after a refused cut retires that seat through its retirement path.

clean

cotal clean <history|store|all> --force
cotal clean <store|all> --dry-run
cotal clean restore-attempt --attempt <id> --force
cotal clean restore-fallback --attempt <id> --force [--discard-history]
FlagDefaultMeaning
--space <s> / --server <url> / --creds <path>resolved meshhistory: target mesh
--dmsoffhistory: also clear DM history
--store-dir <dir>.cotal/natsstore/all: JetStream store directory
--forcenoneRequired: destructive, no prompting
--dry-runoffstore/all: list what would be removed and remove nothing; needs no --force
--attempt <id>nonerestore-attempt: exact stale pre-commit attempt; restore-fallback: matching healthy committed restore
--discard-historyoffrestore-fallback: also delete an old store that holds the only copy of the history or workflow runs the restore left out

One configurable cleanup verb; every target requires --force, except a --dry-run of store or all. It takes one target and no extra positional arguments. A channel name after history refuses USAGE_INVALID (exit 1); it does not narrow the purge. The dry run quotes an explicit --store-dir value in its printed force command so it stays one shell argument. Only history takes --space, --server or --creds. Every other target acts on the root of the folder it runs in, so it refuses them USAGE_INVALID (exit 1) before local cleanup inspects or removes root state.

  • history purges the retained message backlog on the running broker (channels, plus DMs with --dms). The same operation as history clear, which stays as an alias.
  • store deletes the stopped mesh's JetStream store (.cotal/nats): streams, durable consumers, and messages. This is the reset for stale on-disk broker state, e.g. durables minted by an older, incompatible Cotal generation surviving a down/up cycle. It removes the store's jetstream directory, its .cotal-store-id marker and the broker's ocsp folder, then the empty store directory. A store directory that holds anything else refuses CONFLICT (exit 2), names those entries and removes nothing; move them out and run it again.
  • all is store plus the space identity (.cotal/auth), the local creds and markers tied to it, any crash residue a normal down would have swept (stale pidfiles, run/), and the mesh's registry entry; the next cotal up mints a fresh identity. On a root deployed with up -f, plain cotal up then refuses and names cotal up -f <manifest> when the deployment ran at another address, mode or space. A full backup is bound to the space trust it was taken under, so full backups taken before clean all no longer restore into this root. Only the registry of such a backup still restores, with backup restore <dir> --only registry, and chat and DM history start empty. Its dry run and its receipt say so when it removes that trust; take a new full backup after the next cotal up. On a user-auth space it also removes the space's IdP trust and its actor ledger, so every actor grant is lost. Its dry run and its receipt say so, and a later up then needs cotal up --user-auth --idp <auth base URL> and each actor granted again with cotal actor grant.

store and all move the store aside to a .cotal-clean-<id> folder beside it, then remove its entries there, as reset store and reset space do. A removal that stops partway, such as at a folder in the store this account cannot write, ends OPERATION_PARTIAL (exit 4). It names that folder and what is left in it, and its Next makes the folder removable and removes it. Nothing else records the folder, so a later clean or reset finds no store and reports nothing to remove.

An existing store path that is a symbolic link refuses CONFLICT (exit 2), naming the link and its target. Replace the link with the directory it points to, then run the command again. The link and its target are left unchanged, including for --dry-run.

Cleanup eligibility

history needs the mesh up; store and all refuse while any recorded mesh process may still be running or any same-root recorded broker endpoint remains reachable (run cotal down first). On a controller root, a member whose process is gone counts as stopped once a completed down recorded it as already stopped, so down --yes after a crash is enough. A record counts as stopped only when it is absent, empty, or names a pid the kernel reports gone, so unattributable content, a removal reservation, or a pid whose liveness cannot be read refuses. They also refuse outright on a root that holds accounts for several spaces: the store and the broker trust record are shared by every space on the broker, so both targets would take out all of them and no --space can narrow that. down, backup and up --restore refuse there for the same reason. cotal status lists the tenants on such a root. Personas (.cotal/agents) and logs are never touched. Without --store-dir, store and all read the store the root's last broker served, as a start does: while the default .cotal/nats holds no JetStream data and that store does, such as after backup restore --store-dir, they remove that store, and the dry run names it. Custom cleanup targets must contain either the Cotal store-generation marker or a real jetstream/ store directory; filesystem roots, project roots, and Cotal auth/maintenance trees are always refused. A store in the project folder itself, as up --store-dir . makes, refuses CONFLICT (exit 2), since the folder holds the project's own files; its Next names the store's own entries to remove by hand once the space is stopped. Its contents are not checked, so a directory that holds only a jetstream/ directory is removed as a store, whatever that directory holds. The one exception is another space's data: when another space uses the store folder, as up checks it, store and all and their dry runs refuse CONFLICT (exit 2), name that space, its root and its streams, and remove nothing. Keep the folder while either space needs its history; once neither does, stop both and remove it by hand.

In a folder whose .cotal is a link, every target but history, and both dry runs, refuse before anything reads through it: CONFLICT, exit 2, naming the link and where it leads, or TARGET_NOT_FOUND for a link that leads nowhere, which is not passed over to the records of a folder above it. When the link leads to this folder's own records, moved away and linked back, the Next names moving them back, as up does.

In a copy, in a folder moved away from its recorded path, and in the folder a later up took its space's name over from, the store may hold that space's only history. There store and all, and both dry runs, refuse TARGET_UNAVAILABLE, exit 2, before the liveness checks, naming the folder the stack runs from and the step the commands that read there name, and remove nothing.

--dry-run runs the same checks as store or all and prints would remove <path> for each store, credential, record and registration the command would remove, then removes nothing and takes no maintenance lock. A check that would refuse the command refuses its dry run the same way.

Ctrl-C or SIGTERM during store or all releases the maintenance lock, reports what the clean left and exits 130 or 143. The outcome is failed when nothing was removed yet, and unknown, naming the paths removed, once removal began.

store and all also refuse every maintenance journal state, with LIFECYCLE_BLOCKED, exit 2. The refusal names the attempt and, as Next, the step cotal status shows for it, such as bare cotal up to resume a preserved cut; once the root holds no maintenance state and its mesh is stopped, the clean runs. After a healthy committed restore, restore-fallback is the only supported way to remove the recorded unchanged old-store inode; it never deletes the active target, requires both the exact attempt id and --force, and retires the completed restore journal so a later down --preserve-state can start a new backup cycle. After a restore that left history or workflow runs out it also requires --discard-history, as backup cleanup-fallback does, and its refusal names the flag without printing the command help. After an --accept-missing-source restore there is no old store, so it only retires the journal. An attempt the root's maintenance record does not name refuses with TARGET_NOT_FOUND, exit 2, and deletes nothing. A repeat after a completed cleanup is such an attempt, and its refusal says the restore has settled and names cotal status. Without --force, restore-attempt and restore-fallback name the command to run with its --attempt. restore-attempt refuses the same way, with TARGET_NOT_FOUND, for an attempt the record does not name, and with LIFECYCLE_BLOCKED for a recorded attempt past its pre-commit point, which recovers forward. Without --attempt both verbs refuse with USAGE_INVALID. Each refusal's Next names the attempt the root records and its step.

reset

cotal reset store <space> [--dry-run] [-y|--yes] [--json|--plain]
cotal reset space <space> [--dry-run] [-y|--yes] [--json|--plain]

Both routes select a space this machine started. A space with a locally started registration is selected by it. A controller stack that a completed down stopped has no registration; its prepared record and stack journal at the same root name its broker address, store and mode, and a reset keeps both, so a later up starts it again in the same mode. No reset changes a stack's mode; up names the way to run another one. After reset space of a user-auth space, that up needs --user-auth --idp <auth base URL>, as described below. A stack none of whose members ever started has no store to reset, so both refuse TARGET_NOT_FOUND and name cotal up. So does a prepared space no stack has run for, which also names cotal space forget. A stack whose last operation is not a completed down refuses LIFECYCLE_BLOCKED and names cotal down --yes. Each refusal names one reason and the command that clears it. A space that may still run names cotal down from its root, and lists each process that may run once. A stack member whose process also holds its component's record, such as the manager's pidfile, is listed as that member with the record's pid. A member that exited without a recorded stop, as after a crash, is settled by cotal down --yes: it finds the member gone and records it as already stopped, and the reset then accepts it. Only a member that no completed down recorded that way needs cotal up and then cotal down --yes. A legacy root has no stack journal, and its down drops the registration a reset selects, so no reset reaches it once stopped. A space named after its folder keeps that registration, so a reset reaches it stopped. For any other legacy root, the step after cotal down is cotal clean store --force from its root for reset store, or cotal clean all --force for reset space, with --store-dir <dir> when the registration records a custom store. Once stopped, such a space has no record here, like a mistyped name, so a reset of either refuses TARGET_NOT_FOUND, says no registered or prepared space has that exact name, and names cotal space and then that step.

Removing the store removes what the broker kept in it: workflow runs and their journals, channel history, stored messages and every other record. A run still waiting for an answer goes with it and cannot be answered or resumed afterwards. Only the broker reads the store, and it is stopped, so cotal down reads the stack's runs from its manager before stopping it and records them in the stack journal with the steps they wait on. The preview, the confirmation and the receipt list those runs as of the last down, and --json carries them as storeContents.runsAtLastDown and removedRuns. When that down could not read them, or no down recorded them, the preview says so. To see the runs then, run cotal up and cotal run, then cotal down --yes.

Store preview

The store dry-run is a local-impact preview that exits 0. It requires a space selected that way, a real project .cotal and a root readable as one local tenant. It opens with a verdict: the apply is offered, with cotal reset store <space> --yes as Next, or it would refuse, with the apply's own reason and remedy. The JSON result carries it as applyVerdict, and completeness stays partial. Completion suggests locally started names, without proving eligibility. The preview inspects the store path with the existing guarded cleanup path checker, reads the maintenance journal, legacy process records, service-controller instances, supervisor receipts, retained native attempts and root-local additive ledgers and attempts plus fresh boot attempts, and makes a credless broker probe. Readable deployment IDs, unsettled attempts and unreadable record paths remain visible; these records do not prove live ownership or a complete broker tenant inventory. An orphan receipt or attempt remains visible without an instance record; these space-scoped records are not proven owned by this root. The preview also lists other local registrations at the exact recorded broker address. A match blocks store apply, while no match does not exclude aliases, remote tenants or live attachments. A negative credless probe cannot prove absence for TLS-first listeners. A custom store path must be absolute and one real directory. A recorded store path that is a link refuses CONFLICT in the preview and the apply of both resets, naming the path and the directory it points to, and nothing is removed. Replace the link with that directory to reset it. A selected root whose .cotal is a link refuses both resets and their previews CONFLICT, exit 2, before anything reads through it, naming the link, and nothing is removed; when the records were moved away and linked back, the Next names moving them back. A store in the project folder itself refuses both resets CONFLICT as it refuses clean store, naming the store's own entries to remove by hand. The preview takes no maintenance lock and cannot establish exclusive custody or a complete identity deletion set.

Store reset

Bare reset store <space> checks those conditions, displays a scoped terminal confirmation or requires --yes in a script, and acquires the maintenance lock. While another command holds it, such as an up started at the same time, reset store and reset space refuse LIFECYCLE_BLOCKED, exit 2, name that command's pid and remove nothing. Under the lock it rechecks the exact registration or stopped-stack records, one-tenant root, absent maintenance journal, legacy process records, service instances, supervisor receipts and retained native attempts for that space, additive ledgers and attempts plus unsettled fresh boot attempts under the root, credless broker nonresponse and pinned filesystem identity before calling the existing store-only cleanup. A readable or unreadable additive ledger or attempt blocks reset before confirmation and under the lock. It refuses another local registration at the exact broker address before confirmation and rechecks under the lock. This does not prove exclusivity against aliases or remote tenants. An orphan receipt or attempt blocks reset even when no instance record remains; a submitted native step may have changed state. A TLS-required or TLS-first target refuses because a credless probe cannot prove it is down. The route preserves trust, credentials and registration. A cleanup with an unconfirmed outcome is reported as unknown and is not retried. Credless nonresponse and the recorded process checks are not proof that no unrecorded process exists. A store directory that holds anything besides the store refuses both routes and their dry runs as it refuses clean store, naming those entries. A store folder another space uses refuses both routes and their dry runs CONFLICT the same way, naming that space, its root and its streams; the store dry-run shows it as its verdict.

Space identity reset

reset space <space> adds local identity cleanup through the existing native clean-all operation. This route currently requires Linux, a space selected as above and a plain-NATS loopback listener. It removes a locally started registration; a stopped controller stack keeps its prepared record and stack journal. On a user-auth space it also removes the IdP trust and the actor ledger, so every actor grant is lost. The preview and the receipt say so on their own line: a later up needs cotal up --user-auth --idp <auth base URL>, and each actor needs cotal actor grant again. TLS-first, remote, shared-root and non-Linux targets refuse. It applies the same maintenance, deployment and process-custody guards as store reset. It does not stop a process to become eligible.

Its --dry-run validates the existing local trust chain and inventories the native identity paths, including provider-declared secret keys, without running deprovisioning hooks. The inventory is bounded to 10,000 paths. Foreign space segments, symlinks, hard-linked files, foreign ownership, group/world-writable paths and providers without bounded cleanup metadata refuse. The refusal names the path and its reason; for a group- or world-writable path it names chmod go-w <path>. Values and content fingerprints are not printed. No maintenance lock is taken by the preview. Like the store dry-run, it has a Store contents section when a store is present, naming what removing it removes and the runs the last down recorded, and --json carries them as storeContents. The apply's confirmation shows the same section. A preview that passes every check opens with a verdict, as the store dry-run does: the apply is offered, with cotal reset space <space> --yes as Next.

Apply shows the path inventory and defaults to cancellation, or requires --yes in a script. Under the maintenance lock it repeats the eligibility checks and compares the reviewed file identities. Native secret deletion is restricted to the planned keys; each filesystem removal checks the remaining reviewed tree. Added paths and changed files do not enlarge the confirmed scope. The native operation deprovisions secrets before raw identity removal and removes trust last. After checking the planned targets absent, it removes only the unchanged eligible local registration and releases its matching selection. A replacement registration is kept and reported as partial.

Receipt limits

The receipt retains acknowledged deletion paths even if later cleanup fails, and lists each path once.

Ctrl-C or SIGTERM during either apply releases the maintenance lock, reports what the reset left and exits 130 or 143. Before the removal was submitted the outcome is failed and the store and identity are as they were. After it, the outcome is unknown, reset space lists the paths removed so far, and Next is the route's --dry-run. Unconfirmed effects are unknown and are not retried. Personas, saved service definitions and stopped-instance records, installed packages, login sessions and remote data remain. Local cleanup does not revoke a remote credential or clear Cloud data.

A saved service definition whose credentialRef names a credential this space's account issued, such as one cotal auth mint wrote, would hand its child a credential the next identity rejects. Reset reads the issuing account from each such file and clears that credentialRef, and both the preview and the receipt name the service and the file. A file it cannot read as a credential is left as it is. To give the service a credential again, mint one after cotal up, then remove the service and add it again with that credentialRef.

Named services

See the current command contract for this behavior.

Controller stacks

See the current command contract for this behavior.

Delivery shutdown

See the current command contract for this behavior.

Preserved cuts

See the current command contract for this behavior.

Maintenance attempts

See the current command contract for this behavior.

On this page