# Host Update Runtime Identity

Oysterun package updates use one service-owned Node/npm identity. The running
Host's `process.execPath` is authoritative; `PATH` is never allowed to select a
different Node or npm for staging, activation, validation, or restart.

## Service setup

The canonical service install writes `node-npm-runtime-identity.json` in the
active stack Host config directory. The non-secret, atomic record contains the
resolved Node executable/realpath, Node version and module ABI, the matching
npm CLI entrypoint/realpath and version, platform, architecture, resolution
time, and a digest over the runtime identity.

The LaunchAgent and pid-file Host environment carry the exact
`OYSTERUN_NODE_BIN`, `OYSTERUN_NPM_CLI`, and identity path. Re-running service
install is the explicit way to migrate a Host to another Node installation.
An ordinary Host restart verifies the retained identity and fails closed if
the runtime moved or the matching npm CLI disappeared.

## Update transaction

After the Owner confirms Update & Restart, the Host verifies the persisted
identity before registry traffic. It invokes npm only as:

```text
<absolute Host Node> <absolute matching npm-cli.js> ...
```

The child environment places the authoritative Node directory first and sets
the npm-supported Node provenance variables, but those variables and `PATH`
are defense in depth rather than identity authority. A decoy or newer npm in
`PATH` is ignored.

Package download, isolated staging, native/runtime verification, target-config
compatibility, activation, and active-runtime verification occur while the
current Host and dashboard login remain available. The P076 restore snapshot
and P021 maintenance/login gate are created only after preparation succeeds,
immediately before the absolute Oysterun CLI restart handoff. A preparation
failure leaves the old Host/login/session state in place and retains a
sanitized operation result plus deterministic runner log.

Activation retains the prior package, CLI-link target, global package lock,
and a copy of the P076 restore transaction until the canonical service restart
returns healthy under the exact target version. The packaged one-shot restart
transaction runner is the sole owner of `update_complete`. If target restart
or post-restart health/version qualification fails, it stops the failed target,
restores the prior package/CLI/lock and prepared restore state, restarts the
prior package through its absolute CLI, and records `rolled_back`. A failed
rollback preserves the operation tree and records `rollback_failed` for
operator recovery; success or completed rollback removes the operation tree.

Operation logs and structured results sanitize URL userinfo, authorization,
token, password, and cookie material. UI status may expose bounded toolchain
versions/ABI and terminal phase, but never the registry URL, full environment,
dashboard credentials, provider state, or local operation paths.

Synthetic update artifacts must use only a task-owned local registry. This
contract does not authorize npm publication, dist-tag changes, or production
Host mutation.

## One Lifecycle Coordinator

Host Preferences and terminal commands select a transaction kind in the same
P090/P110 coordinator:

```text
Restart Host                    -> restart_only
Update & Restart                -> update_latest_and_restart
Install Beta & Restart          -> update_beta_and_restart
service:restart --restore-sessions
service:update --channel latest|beta
```

`oysterun service:lifecycle-status` reads the same redacted durable operation.
No entry point shells out to an independent bare-npm updater.

Before restore capture or stop, the coordinator verifies the exact stack,
config, port, service definition, package root, persisted runtime identity,
replacement command, static/runtime prerequisites, supported listener
detector, numeric current listener owner, and P110 conflict state. The same
detector performs the final post-stop port recheck. Failure preserves the
current Host, login, sessions, package, service definition, and restore files.

An expired recovery operation remains blocking unless durable evidence proves
that a later fully qualified package is now running healthy under the exact
persisted identity. The reconciliation action writes a separate redacted
terminal receipt, preserves the original result/rollback/cleanup evidence,
then publishes `superseded_reconciled`. Missing or contradictory evidence
stays visibly `Recovery required`; it is never relabeled `Installing...` and
never starts a second updater.
