# npm Native Dependency Install

This document defines how Oysterun packages, installs, validates, and updates
native npm runtime dependencies.

## Ownership

The published `oysterun` tarball contains Oysterun source and prebuilt web
assets. It does not contain a maintainer machine's `node_modules`. npm resolves
and installs production dependencies for the target operating system and CPU.

Native dependencies must use an exact version. A semver range is not allowed
because a newer native release may remove a prebuilt artifact for a supported
Node ABI while still satisfying the JavaScript package range.

Oysterun currently pins:

```text
better-sqlite3 12.8.0
node-datachannel 0.32.3
```

`better-sqlite3` provides the required Node ABI prebuilds.
`node-datachannel` uses N-API 8 and publishes macOS and Linux x64/arm64
prebuilds. This avoids requiring a compiler for the normal supported
fresh-install path.

## Runtime Verification

`tool_scripts/verify_oysterun_runtime.mjs` is included in the npm package and
runs as package `postinstall`. Success requires all of the following:

1. the package is named `oysterun`;
2. the installed package version matches the requested version when supplied;
3. `better-sqlite3` and `node-datachannel` use exact dependency versions;
4. each installed native dependency version matches that exact version;
5. an in-memory SQLite database opens, executes `SELECT 1`, and closes; and
6. the libdatachannel native runtime loads and reports its version;
7. the generated package boundary contains every required Host/client runtime
   path and every packaged JavaScript relative import resolves; and
8. the installed Host connectivity adapter imports from the installed package,
   not from the source worktree; and
9. the exact Claude ACP package, executable runner, transitive dependencies,
   and importable module entry resolve inside the installed package boundary.

Checking only for `package.json`, `binding.gyp`, or a `.node` file is not enough.
The native binary must load under the Node runtime that starts Oysterun.
The verifier also rejects dependency resolution from an ancestor source
worktree. `better-sqlite3` and `node-datachannel` must resolve below the
installed Oysterun package's own `node_modules`.

## Fresh Install

The supported command remains:

```bash
npm install -g oysterun@latest --prefer-online
```

npm installs the platform dependency and then runs Oysterun's postinstall
runtime verifier. Setup and Host service start remain explicit; postinstall
must not create config, migrate Host data, start a service, or spawn a second
provider process that competes with an already-running Host. The release gate
runs the verifier again without `--package-install` to perform the
credential-free ACP initialize probe, followed by a real fresh-Host session
and exact reply.

## Update And Restart

Host Preferences verifies the service-owned Node/npm identity before registry
traffic, resolves an exact target version, and invokes
`tool_scripts/install_oysterun_update.mjs` with the absolute Node path,
absolute matching npm CLI, identity digest, and active package root. It also
passes the active Host config directory explicitly. See
`docs/routec/dev/host_update_runtime_identity.md`.

The installer:

1. requires at least 2 GiB of free space on the global npm prefix filesystem
   before creating an operation directory;
2. installs the target into an isolated prefix;
3. validates target version and opens an in-memory SQLite database;
4. copies the active root config files into an operation-local compatibility
   snapshot;
5. runs the staged target's own `readConfig()` against that snapshot so
   unsupported config/debug keys fail before activation;
6. preserves the active package and global npm metadata;
7. installs the target into the active global prefix;
8. validates the active target again;
9. restores the previous package and CLI link if activation fails;
10. removes the complete `.oysterun-update-*` operation root after successful
    activation or a completed rollback.

The compatibility check never lets staged target code normalize or backfill
the real Host config. It copies `config.json`, `config.debug.json`,
`config.dev.json`, and `cloud_identity.json`, points the staged process at the
copy through `OYSTERUN_CONFIG_DIR`, and removes the copy after validation.

The existing Host process remains running during install. The detached restart
runner is called only after the installer exits successfully. A failed staged
runtime/config validation or active validation must not activate the target,
stop Host, or claim that restart is scheduled.

Host Preferences adds the server-owned update restart handoff only after this
installer has completed staging, native/runtime/config validation, activation,
and active verification. An accepted plain Restart or prepared production
Update/Beta Install response
contains `handoff_started_at`, `execute_not_before`, and `handoff_seconds`.
For an update, `execute_not_before` is 10 seconds after preparation succeeds;
the initial Owner confirmation already authorizes the complete Host-owned
transaction, so closing the dashboard cannot cancel preparation. During the
actual restart handoff, Host
Preferences keeps the existing action region in place, shows the 30-second
restart estimate and a visible 10-to-0 logout countdown, then clears dashboard
authentication and displays login at the deadline. Preparation failures occur
before the P076 snapshot/login gate, leaving the current Host and authenticated
page available.

Insufficient disk space fails before a staging directory is created. Failures
before the active package is moved remove the temporary operation directory.
For a Host-owned restart transaction, successful activation retains the prior
package, CLI link state, global lock, and P076 restore snapshot until the
packaged `oysterun_update_restart_runner.mjs` completes canonical service
restart plus loopback health and exact-version qualification. Restart/health
failure restores and restarts the prior package; only terminal success or a
completed rollback removes the operation directory. `rollback_failed` retains
that tree for explicit operator recovery.
When activation fails and the previous package, CLI link, and npm metadata are
successfully restored, the rollback operation directory is also removed. An
operation root is retained only when rollback itself cannot complete, because
its package trees may be required for manual recovery.

Update/restart runner callbacks release the persistent login gate through a
non-throwing error boundary. If gate persistence itself fails, the error is
logged, but it must not become an uncaught exception that terminates the still
running Host. The replacement Host also retries gate release after listen
readiness.

## Release Gate

Run:

```bash
npm run test:npm-artifact
```

The gate builds the staging package, inspects the tarball boundary, installs it
into an empty prefix, opens SQLite, loads libdatachannel, removes both fixture
native dependencies, and confirms a clean reinstall restores them. It then
starts `host-service/server.mjs` from that fresh installed package with an
isolated HOME/config and random loopback port, and requires `/health` to return
`status=ok`. Linux Node 20 must also be tested on a clean remote prefix before
publish.

Do not validate by loading native dependencies from the source worktree. The
test must load them from the newly installed package root.

## Legacy Updater UAT

Before publishing a native-dependency release, run one bounded Linux UAT from
an npm version that predates the new transactional installer:

1. begin with an empty Host config folder and a clean global install;
2. install and set up the old Oysterun version;
3. create a real provider session and commit assistant replies;
4. make the candidate tarball available as an exact version through a
   temporary registry that proxies all other packages to the public npm
   registry;
5. invoke the old Host's authenticated update-check and update-run endpoints;
6. verify candidate version, native module versions, in-memory SQLite, WebRTC
   native library load, automatic Host storage migration, preserved messages,
   restored session, and a new provider reply after upgrade;
7. restart without the registry override and remove every temporary registry
   artifact.

This validates the first upgrade boundary: the old updater cannot use code that
exists only in the candidate release, so the candidate package's exact native
dependency and postinstall verifier must be sufficient on their own.
