The jarvis-research command¶
jarvis-research is the lifecycle command for a JARVIS install. The installer
(./setup.sh) puts a small launcher on your PATH
at ~/.local/bin/jarvis-research and records your checkout as the managed
install, so you can run day-to-day operations from any directory.
The launcher carries no logic of its own: it finds your most recently installed checkout and hands off to that repository's tracked script. An update therefore ships a newer command along with the rest of the code.
If jarvis-research is not found after an install, make sure ~/.local/bin is
on your PATH. Setup checks this against the shell it runs in and prints the fix
if that shell cannot see the directory. A new terminal reads your startup files
instead, so if the command is missing there, add the PATH line to your shell's
startup file.
Commands¶
| Command | What it does |
|---|---|
update [--to <tag>] [--resume <tag>] [--yes] |
Transactional, database-safe upgrade to the latest published release, or to a specific --to tag. Refuses on a diverged, dirty, or non-main checkout and when the target's images are not yet published. |
status |
Show container status (docker compose ps). |
start |
Start the stack without building (docker compose up -d --no-build). |
stop |
Stop the stack. |
restart |
Restart the stack. |
logs [args] |
Tail service logs; extra arguments pass through to docker compose logs (e.g. logs -f paper_ingestion). |
doctor |
Read-only health, disk, registration, and update-availability check, plus host preflight probes. |
repair |
Bounded, non-destructive recovery: recreate stopped containers (no build, no pull) and restart any unhealthy mandatory service. |
owner status |
Show whether instance ownership comes from the host environment or database and whether it resolves to a live administrator. |
owner set <email> |
Repair a missing or invalid database-managed owner. Refuses host-managed ownership and requires the target email to be typed again. |
restore status |
Report the queued or most recent transient restore: state, current step, error, whether manual follow-up is required, and its safety backup. |
restore legacy <timestamp> [--allow-unknown-schema] |
Restore a backup taken on this host before manifest signing. The set cannot be checked for tampering, so the acceptance phrase must be typed at the prompt. Add --allow-unknown-schema only when the chosen backup recorded no usable database schema version (it was written while the database was unreachable) and the restore refuses it for that reason. Off-site sets are never eligible. See Backup & Restore. |
restore request <timestamp> |
Print the ordered steps and the ready-made request for recovering this host from another installation's backup set. It submits nothing. |
restore acknowledge <restore-id> |
After an off-host restore, release outbound-integration quarantine for the exact reviewed restore. Requires the restore ID to be typed again. |
register |
Record the current checkout as the managed install and refresh the launcher. |
uninstall [--dry-run] [--tier N] [--keep-data] [--keep-images] [--all] [--yes] |
Tiered, contained teardown of a managed install: stop (1), remove application images (2), delete data volumes (3), or full purge (4). Lead with --dry-run. See Uninstalling. |
version |
Print the command name and the installed JARVIS_VERSION. |
help |
Print usage. |
Run jarvis-research help for the built-in summary. A command can be pointed at
a specific checkout with jarvis-research --repo <dir> <command>.
Ownership recovery¶
Run jarvis-research owner status after upgrading an installation that already
had multiple administrators. Migration 0105 assigns the sole live
administrator automatically, but deliberately leaves a multi-admin choice to
the operator. If the database owner is missing or invalid, use
jarvis-research owner set <admin-email> and type the same email to confirm.
The command accepts only a live administrator and commits the repair atomically.
When OWNER_USER_ID manages ownership, change that host setting and restart
JARVIS instead. A valid database-managed owner transfers ownership in Admin →
User Management, not through the repair command.
Restore acknowledgement¶
An off-host restore quarantines restored outbound integrations until their
credentials have been reviewed. Use the browser progress view or run
jarvis-research restore acknowledge <restore-id> with the exact current
32-character restore ID, then type it again. A missing, stale, or changed review
state is refused and leaves quarantine active. See Backup &
Restore.
Exit codes¶
| Code | Meaning |
|---|---|
0 |
Success. |
1 |
The operation was refused or failed. |
2 |
Usage error (unknown command or option). |
3 |
Environment problem — the Docker daemon is not reachable. |
How update works¶
jarvis-research update is transactional: it verifies the whole target release
before it changes anything, advances your checkout only by a fast-forward, and
records its progress so an interrupted run can resume. It never force-rewrites
your branch.
In order, an update:
- Checks preconditions. The checkout must be registered, its
originmust be the managed JARVIS repository, the Docker daemon must be reachable, and the checkout must be a clean, non-detachedmain(uncommitted changes, a detached HEAD, or a working branch are all refused with guidance). - Selects a target. With no argument it picks the highest published stable
release;
--to <tag>selects a specific tag. - Requires a fast-forward. If your checkout has diverged from the target so that a fast-forward is impossible, the update is refused rather than forced. If you are already on the target, it reports "already up to date".
- Verifies the release is fully published. Every registry-backed image the target needs must already exist; a visible tag whose images are still uploading is refused, so you never advance onto a half-published release.
- Enforces a restore point before a data-changing migration. New migrations between your version and the target are inspected. If any one changes data, the command triggers a backup and accepts it only when its signed manifest names the exact main-database, model-router, uploaded-PDF, and secrets archives for that run and their sizes and checksums match. A missing, unsigned, incomplete, or mismatched point is refused. Additive-only migrations apply on restart and only suggest taking a restore point first.
- Stages images, then advances. The target's Compose files are resolved with this install's active profiles. Every exact registry image in that result, including profile dependencies and target-added services, is pulled before the branch moves. Services explicitly marked for local builds are not sent to a registry. A failed pull leaves the checkout untouched.
- Applies and verifies health. It pins
JARVIS_VERSION, recreates the services (viaupdate.sh), and waits for them to report healthy. On success it clears the pending-transaction record and prints adoctorsummary.
Resuming an interrupted update¶
Each install writes its own small pending-transaction file, so interrupted
updates in two clones cannot overwrite each other. On the first update after
upgrading from an older command, JARVIS moves the former shared record only when
its recorded commits identify this install unambiguously; otherwise it stops and
keeps the record untouched. If a run is interrupted, run
jarvis-research update again — it
resumes deterministically from the recorded phase instead of reporting "up to
date", even when the checkout already points at the target commit. A run that
stopped before advancing the branch restarts cleanly. If you need to drive the
post-advance half explicitly, jarvis-research update --resume <tag> runs only
those remaining steps. The command refuses to resume if the checkout moved to
an unexpected commit.
Updating from a release before v1.2.2¶
Maintained in-place update support starts at v1.2.0. Installations running
v1.2.0 or v1.2.1 update to the current release in one step through the target
release's update bootstrap, which is the bootstrap path in the release support
table. The procedure below is the immutable legacy bridge for installations
older than the maintained window — v1.1.3 and earlier — which is no longer in
the maintained source matrix; a direct jump from v1.1.3 to the current release is
not supported. To take the one-step path, run the same bootstrap command shown
below with the release you are updating to in place of v1.2.2, in both the
download URL and --to.
Installations first set up before v1.1.3 need one extra step before the bootstrap
below. v1.1.3 added an SMTP password to the deployment's Docker secrets, and no
updater before v1.2.4 created that file, so docker compose up aborts with
secret not found part way through replacing containers. Create the placeholder
once, from the installation directory:
mkdir -p secrets
[ -f secrets/smtp_pass.txt ] || { : > secrets/smtp_pass.txt; chmod 644 secrets/smtp_pass.txt; }
An empty file is the correct "SMTP password not configured" value; if you send
mail, setup.sh --smtp-pass-file writes the real password to the same path. From
v1.2.4 onward the updater creates the secret files it manages before it pulls,
builds or replaces anything, so this step is only needed once, on the way out of
a pre-v1.1.3 installation.
The lifecycle commands shipped with v1.1.3, v1.2.0 and v1.2.1 all predate the
backup protocol later releases require, which is why the bootstrap loads the
target release's lifecycle files before updating. From the installation
directory, run it once. An installation on v1.2.0 or v1.2.1 substitutes the
release it is updating to for v1.2.2 in both places below and lands on that
release in one step; v1.1.3 and earlier run the immutable bridge exactly as
written and then follow the bootstrap path:
(
set -e
bootstrap="$(mktemp)"
trap 'rm -f "$bootstrap"' EXIT
curl -fsSL -o "$bootstrap" \
https://raw.githubusercontent.com/limitcycle-oss/jarvis-rd-assistant/v1.2.2/scripts/update-bootstrap.sh
bash "$bootstrap" --repo "$PWD" --to v1.2.2
)
The bootstrap accepts only the managed repository on a clean main checkout,
validates the release you named against origin, and runs the lifecycle files
stored in that release. That release's updater then creates and authenticates a
restore point containing both databases, uploaded PDFs and data-coupled secrets
before it can apply a data-changing migration. If the command is interrupted, run the same
bootstrap command again; it resumes the recorded update.
Your installed command may refuse first with Your working tree has uncommitted
changes; refusing to update. Run the bootstrap above anyway: it knows about the
backup service's secrets/manifest-hmac-required file and leaves it alone, and if
anything else is blocking the update it names the exact paths.
Never delete secrets/manifest-hmac-required. The backup service creates it to
record that this installation requires a signed backup manifest, and removing it
would let a later restore accept an unsigned one.
Installations already running v1.2.2 or later update normally with
jarvis-research update.
Rolling back¶
update never rolls back on its own. When an update fails after the images
changed, it prints the exact commands to pin JARVIS_VERSION back to your
previous version and pull those images.
If a data-changing migration already ran, image rollback alone is not
schema-safe — the new database schema stays in place. To return to the
pre-update state, restore the backup taken before the update; that rolls the
database back together with the images. Use the WebUI Backup panel → Restore,
and follow it with jarvis-research restore status. If the surviving set
predates manifest signing, jarvis-research restore legacy <timestamp> accepts
it deliberately; to recover from another installation's set,
jarvis-research restore request <timestamp> prints the procedure. See
Backup & Restore.
Release-candidate tags are throwaway¶
An operator who checks out a release candidate with --to <rc-tag> is on a
scratch checkout. Because a release squash-merges its work, the stable tag lands
on a different commit than the rc, so an rc checkout cannot fast-forward to the
stable tag — the diverged-checkout refusal in step 3 fires by design. A normal
jarvis-research update never selects rc tags; it only considers stable
vX.Y.Z releases. To run a stable release after trying a candidate, do a fresh
install rather than trying to update the rc checkout in place.
Updating by hand¶
jarvis-research update is the supported path for v1.2.2 and later; earlier
releases use the bootstrap above. Use the lower-level fallback only to repair a
lifecycle command that cannot run, and only after independently verifying a
current restore point:
The fallback does not classify migrations, require a signed restore point, or resume a recorded transaction. It is not a migration-safe substitute for either supported update path.
Uninstalling¶
jarvis-research uninstall removes an install in four escalating tiers. Each tier
enumerates the exact containers, images, volumes, and files it will remove and
confirms before acting — nothing is removed by a bulk docker … prune. Preview any
teardown first, which mutates nothing:
| Tier | What it removes | Reversible? |
|---|---|---|
1 stop |
Containers and the jarvis network (docker compose down). Data, images, and files are kept. |
Yes — jarvis-research start. |
2 app |
Tier 1 + the JARVIS application images (ghcr.io/limitcycle-oss/jarvis-*) at your pinned version. |
Yes — a reinstall pulls them again. |
3 data |
Tier 2 + the project's named data volumes (docker compose down --volumes): the database, the vector store, and the caches. |
No. Back up first. |
4 purge |
Tier 3 + the pinned third-party images (each confirmed individually), .env, secrets/, shared/, this install's registry line, the jarvis-research launcher (only if no other install remains), and finally the clone directory itself. |
No. |
Flags¶
| Flag | Effect |
|---|---|
--dry-run |
Enumerate everything the chosen tier would remove and exit without changing anything. |
--tier N |
Run tier N (1–4) directly instead of the interactive menu. |
--keep-data |
Cap the run at tier 2, so the data volumes and on-disk files are always preserved. |
--keep-images |
Remove everything the selected tier covers except images: no application image is removed, no third-party image is confirmed or removed, and the plan lists none. Because no image ref has to be built, the run also proceeds when the installed version in .env is missing or invalid — which is otherwise a refusal at tier 2 and above. |
--all |
Select tier 4 (full purge). It selects a tier and nothing else: the ordinary prompts below still run. |
--yes |
Skip the two ordinary [y/N] prompts (the proceed confirmation and, at tier 3 and above, the backup offer). Requires an explicit --tier N (or --all). |
What a purge asks, in order¶
A tier-4 run asks two ordinary [y/N] prompts and then three mandatory gates:
- Proceed with this uninstall? (ordinary — suppressed by
--yes) - Capture a backup before deleting the volumes? (ordinary, and only when the
stack is running — suppressed by
--yes) - Type the compose project name. (mandatory)
- Export the backup encryption key, or type the acknowledgement phrase. (mandatory)
- Confirm each third-party image. (mandatory; not asked under
--keep-images, which removes none of them)
The destructive gates require typed confirmation¶
--yes skips only the two ordinary confirmations. Neither it nor --all can
satisfy the mandatory gates, each of which reads a typed confirmation from stdin:
- Tier 3 requires typing the compose project name before any volume is deleted. When the stack is running, an interactive run first offers to take a backup inside the backup service, where the required credentials and storage are available. If you accept and that backup fails or another backup is still in progress, uninstall stops before the typed deletion gate.
- Tier 4 first offers to copy the backup encryption key
(
secrets/backup_encrypt_key.txt) to a path outside the clone. That key is deliberately excluded from backup archives, so deletingsecrets/without it leaves every encrypted off-host backup permanently unrecoverable. If you decline the export you must type an explicit acknowledgement phrase to continue. - Tier 4 confirms each third-party image (postgres, ollama, qdrant, caddy, …) individually, since those images may be shared with other projects on the host.
A run with no controlling terminal (closed stdin) therefore cannot complete tier 3
or tier 4 — the install is left untouched. Mind the exit code when you script
this: without --yes the run stops at the proceed confirmation, prints
Aborted; nothing was done. and exits 0, because declining is not an error.
With --yes it reaches a mandatory typed gate, which refuses and exits 1. A
wrapper that only checks the exit status will read the first case as a completed
uninstall, so check the output — or pass --yes and drive the typed gates.
When Docker is not running¶
Every tier needs the Docker daemon. If it is unreachable, uninstall refuses with
exit code 3 and prints an inventory of the orphaned containers, volumes, network,
and on-disk files so you can start Docker (then re-run) or clean up by hand. There
is no file-only teardown path.