You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
cli: tell users how to update, for the way they installed #1245
There are five ways to install Omnigraph: install.sh/install.ps1, install-source.sh, Homebrew, Nix and Docker. Each one updates differently. To update, a user has to remember which one they used and find the matching command in docs/user/install.md. Nothing tells them a new version exists.
#79 added a self-updater. It was closed because a CLI that replaces its own binaries does not fit a project that also ships a server. This proposal replaces no binaries.
Proposal
Install receipt.install.sh, install.ps1 and install-source.sh write .omnigraph-install.json next to the binaries: method, channel (stable, edge or a pinned VERSION), install dir, SHA-256 of omnigraph. The CLI trusts it only when current_exe() is in that dir and the hash matches.
Docker: the Dockerfile sets OMNIGRAPH_INSTALL_METHOD=docker.
Anything else: unknown.
Update notice. At most once a day, on stderr. Off when CI is set, stderr is not a TTY, or OMNIGRAPH_NO_UPDATE_CHECK=1. The check runs in the background and never delays a command.
omnigraph 0.15.0 is available (you have 0.14.1).
Update: brew upgrade ModernRelay/tap/omnigraph
0.15 changes the HTTP contract (0.14 → 0.15). Upgrade omnigraph-server too.
The last line appears only when the new release changes the HTTP contract or the storage format.
omnigraph update. Runs the method's own updater after confirmation:
script: rerun the installer with the receipt's channel and install dir
Homebrew: brew upgrade ModernRelay/tap/omnigraph
Nix, Docker, unknown: print the instruction, run nothing
Out of scope
Replacing binaries from inside the CLI
Updating or restarting omnigraph-server
Version pinning per graph or per cluster
Open questions
Where the check reads the latest release: GitHub Releases API, or a small JSON published with each release that also lists its HTTP contract and storage format
Whether edge installs get the notice. A new edge build publishes daily, so it would show almost every day.
A user who installed Omnigraph has to remember the install method in order to update. The method decides the command. The CLI never says that a newer release exists, and it never says when that release changes the HTTP contract or the storage format. The CLI and the server must move together. omnigraph version already prints the contract and the format of this binary. It does not contact a server.
This is a missing feature. It is not a defect in a command that already exists.
Checked again just before this comment. Issue #1245 is open. It has no comments. It has no linked pull request. The timeline has one event: the needs-triage label at 2026-11-04 13:14Z.
Reproduction
Checkout: main at 40bb2a223b9b9958a212857076f3602b5d8f1702 (2026-11-04 06:05Z, "fix(engine): read a commit of a deleted branch while a live branch retains it (#1240)"). git fetch origin main left HEAD and origin/main on that SHA. The worktree was clean.
Host: Linux 6.12.94+ x86_64, rustc 1.97.1. brew, nix, docker, and PowerShell are not installed. /nix cannot be created (Permission denied). macOS and Windows were not run. The installer scripts were not executed against GitHub. A live install would download binaries and write an install directory. The script text is enough to see that they write no receipt.
Binary: target/debug/omnigraph in this checkout (ELF x86-64, BuildID b4b4fdce5c9bc7dd3c3ac29847bd908aa1c6b7eb). It was not rebuilt for this check. It reports package 0.14.1 with channel null and commit null. That is the unstamped source-build shape in crates/omnigraph-api-types/src/lib.rs. The output matches the source at this SHA.
omnigraph version (stdout and stderr separated, then again with stderr on a pseudoterminal):
exit 0
stdout:
omnigraph 0.14.1
internal-schema 16 (serves v14 to v16)
http-contract 0.14
stderr: empty (0 bytes), including when stderr is a TTY
omnigraph version --json prints the object below and writes nothing to stderr. omnigraph --version prints only omnigraph 0.14.1.
CI=true, OMNIGRAPH_NO_UPDATE_CHECK=1, and OMNIGRAPH_INSTALL_METHOD=docker leave that stdout unchanged and leave stderr empty. Those variables do not exist in the CLI.
omnigraph update exits 64:
error: unrecognized subcommand 'update'
tip: a similar subcommand exists: 'upgrade'
omnigraph --help lists upgrade (offline storage conversion) and version. It has no binary-update command.
Path detection, on this Linux host only. A small program printed std::env::current_exe() and std::fs::canonicalize. A symlink at /tmp/og-path-exp/bin/showexe pointed at /tmp/og-path-exp/Cellar/omnigraph/0.14.1/bin/showexe.
An extra symlink outside that tree resolved the same way. A copy of the program outside Cellar resolved to its own path (cellar=false). On this host, current_exe() already returns the symlink target, because Linux exposes /proc/self/exe. That is not a macOS result. Homebrew's main bottle is macOS arm64. This host has no Homebrew cellar.
A failing test that expects an update notice is not the right proof. The notice was never part of the contract. version_command_prints_cli_version_http_contract_and_storage_formats_issue_977 in crates/omnigraph-cli/tests/integration/cli_schema_config.rs pins the current version text. A notice on stdout would fail that test. A notice on stderr would not. The runs above are the user-visible proof.
Claims
Confirmed. There are five install channels, and they update in different ways.
docs/user/install.md documents the shell installer, Homebrew, Nix, Windows install.ps1, and install-source.sh. The container is documented there as an image and in docs/user/deployment.md. The image also contains the CLI: the Dockerfile copies omnigraph to /usr/local/bin/omnigraph. A manual cargo build is documented beside the source installer. npm packages are clients. docs/dev/versioning.md says current binaries ship through the installer, Homebrew, Docker, and GitHub Releases, and it says not to document cargo install.
What each channel does today:
scripts/install.sh downloads a GitHub release archive, checks the archive SHA-256, and copies omnigraph, omnigraph-server, and omnigraph-azure-admission into INSTALL_DIR (default ~/.local/bin). The default channel is stable, with a fallback to edge. RELEASE_CHANNEL=edge and VERSION=<tag> select a channel. The script prints the channel. It does not store it.
scripts/install.ps1 does the same for omnigraph-windows-x86_64.zip into %USERPROFILE%\.local\bin. Parameters are -ReleaseChannel, -Version, and -InstallDir.
scripts/install-source.sh builds the local checkout, or clones SOURCE_REF (default main), and installs the same three binaries. It records no ref.
Homebrew: Formula/omnigraph.rb in ModernRelay/homebrew-tap installs the three binaries with bin.install and points at the v0.14.0 archives. .github/workflows/release.yml has an update_homebrew_tap job. The user command is the normal brew upgrade ModernRelay/tap/omnigraph. This assessment did not run brew.
Nix: flake.nix builds the three release programs from source. install.md shows nix build and nix run, and a pinned github:ModernRelay/omnigraph/<ref>. It does not show nix profile upgrade.
Docker: pull a new tag or digest. install.md says latest tracks stable, edge tracks the rolling edge release, and a deploy should pin a digest. The entrypoint starts omnigraph-server. It does not update the CLI.
Partly confirmed. The user can find the update command in docs/user/install.md.
The page tells the user how to install. It has no update section. A search of docs/ finds no brew upgrade, no nix profile, and no docker pull. Re-running install.sh is the same text as the install. The Homebrew, Nix, and Docker update commands are not written down. docs/user/operations/upgrade.md is the storage-format procedure (omnigraph upgrade). It is not a binary update.
Confirmed. Nothing in the product tells the user that a new release exists.
omnigraph version is local. docs/user/install.md says it reports what the binary speaks, without opening a graph or contacting a server. There is no startup notice, no OMNIGRAPH_NO_UPDATE_CHECK, and no install receipt. Release pages exist on GitHub. The CLI does not point at them.
Partly confirmed. The sample notice.
The lines in the issue (0.15.0 is available, you have 0.14.1, HTTP contract 0.14 to 0.15) are an example. They are not the live release state. GitHub releases/latest is v0.14.0, published 2026-10-22. The package version in this tree is 0.14.1 (crates/omnigraph-cli/Cargo.toml). There is no 0.15.0 release. The rolling edge release currently holds edge-20261103-03e0b62. Its body says storage format 16 and HTTP contract 0.14, the same as v0.14.0. A correct notice today would not print a contract-change line for that pair.
Confirmed, with a narrower close reason. #79 added a self-updater and was closed.
#79 is pull request #79, not an issue. Title: "feat(cli): add self-update mechanism (omnigraph update)". Closed 2026-05-12, not merged. It downloaded a release, verified SHA-256, and replaced omnigraph and omnigraph-server when they were in the same directory. It also added a background stderr notice.
The close comment from @aaltshuler says the shape does not fit the project. The stated blocker is the adjacent-server swap: omnigraph update replaces the on-disk server binary while the process keeps the old file. The mismatch appears at the next restart, with no log line. The same comment records a smaller direction: omnigraph version --check reads GitHub Releases and prints whether a newer release exists. Docs point CLI users at brew upgrade or the install script, and server operators at their orchestrator. The comment says no self-replacement, no background notifier, and no hidden subcommand.
The new proposal keeps the download-and-rename code out of the CLI. The script branch still runs install.sh. install_from_dir in scripts/install.sh copies omnigraph-server into the same directory. That is the same on-disk swap, done by the installer the CLI would start.
Issue #11 proposed that self-updater. It is closed as completed (2026-04-12) and has no comments. The implementation never landed. Closing #11 does not mean the feature is on main.
Confirmed. The receipt, the path rules, and the Docker variable are absent.
No file under this SHA mentions .omnigraph-install.json, OMNIGRAPH_INSTALL_METHOD, or /Cellar/omnigraph/. The only omnigraph-install strings in the three scripts are temporary directory names. The Dockerfile sets ENV OMNIGRAPH_BIND=0.0.0.0:8080 and no install method. docker/entrypoint.sh starts the server. It does not export an install method.
Partly confirmed. Edge builds publish daily.
docs/user/install.md says edge builds are prereleases built once a day from the tip of main, after CI passes, when main has moved. Recent tags match that pattern (edge-20261103-03e0b62, edge-20261103-36d3ea0, edge-20261101-3e0e467, edge-20261031-269299b). A quiet main does not get a build. A notice on every new edge tag would appear on almost every such day.
Root cause
The CLI has one version report and one storage-upgrade command. They answer different questions.
VersionOutput::current in crates/omnigraph-cli/src/commands/local/output.rs copies the compiled version, OMNIGRAPH_RELEASE_CHANNEL, OMNIGRAPH_SOURCE_VERSION, HTTP_API_CONTRACT ("0.14"), and the storage stamps. write_version prints those facts to stdout. main does not spawn a checker. The command enum in crates/omnigraph-cli/src/cli.rs has Version and Upgrade. Upgrade calls omnigraph::db::upgrade_storage_as_with_storage. It converts a standalone graph at stamp 8, 9, or 13, or it stamps 14 or 15 up to 16. It does not replace a binary.
The stamps are INTERNAL_MANIFEST_SCHEMA_VERSION = 16 and MIN_SUPPORTED_INTERNAL_SCHEMA_VERSION = 14 in crates/omnigraph-catalog/src/migrations.rs. docs/dev/versioning.md says the HTTP contract must match exactly, and the CLI, the server, and the integrations move together. There is no old-client fallback.
The release workflows already know these facts. scripts/edge_release.py reads omnigraph version --json and writes the storage format and the HTTP contract into the edge release body, next to the latest stable release. The stable v0.14.0 notes say in prose that the HTTP contract changed. The v0.14.0 assets are the four archives and their .sha256 files. There is no JSON manifest. The GitHub "latest" API returns a tag and a markdown body. It has no http_contract field and no storage-format field. Parsing that prose is a brittle contract.
Two install trees also explain the receipt. install.sh and install-source.sh both write ~/.local/bin. After that copy, the path does not say which script ran, which channel was selected, or which tag was pinned. Homebrew and Nix can be inferred from a resolved path. Docker cannot. /usr/local/bin/omnigraph is also a normal copy.
The name update collides with upgrade. The CLI already points a typo for update at the storage conversion.
Recommended fix
The user need is two questions. "Is there a release newer than this binary?" "What command updates the method that installed it, and must the server move too?"
The smallest design that answers both, and that does not start an installer:
Add an "Update" section to docs/user/install.md. Give one command per channel. Say that install.sh and install.ps1 also replace omnigraph-server and omnigraph-azure-admission. Say that a running server keeps the old file until the operator restarts it. Point server installs at the orchestrator, the image tag, or the package manager.
Keep omnigraph version offline, as the install guide promises. Add omnigraph version --check. The user asks for it. It performs one HTTPS GET of a small release file, then it prints the method's command. It writes no file. It starts no installer. It does not run on query, mutate, cluster apply, or server boot. Exit 0 when the check completes, whether or not a newer release exists. A network failure is a message on stderr and a non-zero exit for that flag only.
Publish omnigraph-release.json as a release asset, generated from omnigraph version --json in the release workflow. The edge workflow already runs that command. The file carries the tag, the package version, the channel, http_contract, and internal_schema.writes / serves. The CLI compares those fields with its own compiled facts. It prints the contract line only when http_contract or writes differs. The GitHub Releases API remains the index of tags. It is the wrong source for the contract line. Do not parse release notes.
For the shell installers only, write .omnigraph-install.json beside the binaries: method (script or source), channel (stable, edge, or a pinned tag), and install directory. Trust it only when the resolved current_exe() is inside that directory and the recorded SHA-256 matches the running binary. Record the hash of each installed binary, not only omnigraph. A CLI and a server from different releases are the failure this notice exists to prevent. The fields are data. The file has no URL. The CLI must not pass the fields through a shell. A hash mismatch means "unknown". The receipt is not a signature. A person who can write the install directory can replace the binary and the receipt. That person can already replace the binary.
Detection when there is no trusted receipt. Use the resolved current_exe() path. On Linux, current_exe() already follows symlinks. Test path components, not a raw substring of an untrusted string, and confirm the macOS result before relying on /Cellar/omnigraph/. The Homebrew formula's prefix is the Cellar prefix. Also accept the Linuxbrew prefix. Nix: the resolved path is under /nix/store/. This was not executed in a store. Docker: do not use an environment variable as proof. The image can document the pull command. An unknown path prints that generic command. An env var is easy to set and easy to forge, and the Dockerfile does not set one today.
Edge. Do not notify on each edge build. Notify when the stable manifest is newer than this package version, or when its HTTP contract or storage stamp differs. A pinned edge-<date>-<commit> install stays on that pin until the user asks.
Do not add omnigraph update as a command that runs install.sh, install.ps1, or brew upgrade. That command replaces the server binary beside the CLI. It is the behavior PR feat(cli): add self-update mechanism (omnigraph update) [MR-612] #79 closed to avoid. A print-only hint under version --check does not need a second verb. upgrade must stay the storage command.
omnigraph-server, the container entrypoint, and cluster operations do not check for releases. A check inside the image would run from production hosts. Air-gapped and CI machines stay quiet because they never get a background check. An explicit --check is the only network read, so a missing network cannot change query or cluster apply.
Windows is a real channel (install.ps1, omnigraph-windows-x86_64.zip). Any receipt and any printed command need a Windows test. This assessment did not run one.
A --check JSON object would be a new CLI contract. docs/rfcs/README.md asks for an RFC before a new CLI contract. Human text can land with the docs change. Stable JSON should wait for the RFC. Tests belong in the existing version owner, cli_schema_config.rs. Path tests can use a temporary symlink, as in the experiment above. They must not call the live GitHub API.
Alternatives and liability
A docs section alone removes the "which command?" gap. It adds no runtime behavior. It leaves "does a new release exist?" to the user.
A static line on omnigraph version can repeat the install guide: this command does not look for a newer release, and the update commands are in the install guide. The line is local and permanent. It still does not know that a release exists.
Package-manager text alone misses the curl installer. That installer is the first method in the README.
A background daily notice adds a network client, a cache, TTY and CI rules, and a second record of "what the latest release is". Review comments on PR #79 already had to chase cache identity and retry timing. The cache can be wrong, and every successful command pays for the process. The privacy cost is a GET from the user's network. The GET must not send a graph path, a token, or an actor. A static release file does not need the current version in the query. The command must still honor an HTTPS proxy and must give up on a short timeout. That behavior is justified only when the user asked for the check.
The full proposal (receipt, background notice, and omnigraph update that runs the installer) removes the memory burden. It adds a receipt format and a command whose script path changes the server file. Five more changes of that kind become a second installer inside the CLI. The release asset and omnigraph version --json are already the source of the contract and the format. The check should read that source when asked, and it should print. It should not grow a writer.
The trade is deliberate. The automatic notice is the part we drop. A database command must not contact the network, and it must not change a server file, unless the user asked for that operation. Convenience of a hint on every run is the lower priority.
Lance
Lance does not affect this issue. Omnigraph pins unmodified Lance 13.0.0 (Cargo.toml, Cargo.lock). That tag is https://github.com/lance-format/lance/releases/tag/v13.0.0 (2026-10-07). Newer upstream tags include stable https://github.com/lance-format/lance/releases/tag/v14.0.2 (2026-10-29) and prerelease https://github.com/lance-format/lance/releases/tag/v15.0.0-beta.9 (2026-11-03). A search of open lance-format/lance issues for a self-update, an update notifier, or an install receipt found nothing. The storage format in this proposal is Omnigraph's manifest stamp (16, serves 14 through 16), which omnigraph version already prints. docs/dev/versioning.md keeps storage upgrades and the Lance analyzer boundary separate. A Lance bump would not tell a user how they installed Omnigraph. No Lance page is the matching domain for an install-method notice.
No RFC covers an update protocol or a release-manifest file. The compatibility rules are in docs/dev/versioning.md (exact HTTP contract, upgrade the CLI and the server together).
scripts/edge_release.py is the existing generator of the contract and format comparison. A manifest should come from that path, not from a second hand-written table.
Supersession
No open issue supersedes #1245. Issue #11 and PR #79 are the rejected self-replacing updater. They are closed. They are context. They are not a broader open tracker. This issue should stay open.
Limitations
macOS current_exe() was not measured. Homebrew, Nix, Docker, and Windows were not executed. The install scripts were read in full and were not run. The debug binary was not rebuilt at this SHA. Its version output matches this tree. This comment does not change the repository.
Activity
ragnorc commentedon Nov 4, 2026
Summary
A user who installed Omnigraph has to remember the install method in order to update. The method decides the command. The CLI never says that a newer release exists, and it never says when that release changes the HTTP contract or the storage format. The CLI and the server must move together.
omnigraph versionalready prints the contract and the format of this binary. It does not contact a server.This is a missing feature. It is not a defect in a command that already exists.
Checked again just before this comment. Issue #1245 is open. It has no comments. It has no linked pull request. The timeline has one event: the
needs-triagelabel at 2026-11-04 13:14Z.Reproduction
Checkout:
mainat40bb2a223b9b9958a212857076f3602b5d8f1702(2026-11-04 06:05Z, "fix(engine): read a commit of a deleted branch while a live branch retains it (#1240)").git fetch origin mainleftHEADandorigin/mainon that SHA. The worktree was clean.Host: Linux 6.12.94+ x86_64, rustc 1.97.1.
brew,nix,docker, and PowerShell are not installed./nixcannot be created (Permission denied). macOS and Windows were not run. The installer scripts were not executed against GitHub. A live install would download binaries and write an install directory. The script text is enough to see that they write no receipt.Binary:
target/debug/omnigraphin this checkout (ELF x86-64, BuildIDb4b4fdce5c9bc7dd3c3ac29847bd908aa1c6b7eb). It was not rebuilt for this check. It reports package0.14.1withchannelnull andcommitnull. That is the unstamped source-build shape incrates/omnigraph-api-types/src/lib.rs. The output matches the source at this SHA.omnigraph version(stdout and stderr separated, then again with stderr on a pseudoterminal):omnigraph version --jsonprints the object below and writes nothing to stderr.omnigraph --versionprints onlyomnigraph 0.14.1.{ "version": "0.14.1", "channel": null, "commit": null, "http_contract": "0.14", "protocols": { "studio": 1, "target": 2, "credential": 2, "access": 2 }, "internal_schema": { "writes": 16, "serves": [14, 16] } }CI=true,OMNIGRAPH_NO_UPDATE_CHECK=1, andOMNIGRAPH_INSTALL_METHOD=dockerleave that stdout unchanged and leave stderr empty. Those variables do not exist in the CLI.omnigraph updateexits 64:omnigraph --helplistsupgrade(offline storage conversion) andversion. It has no binary-update command.Path detection, on this Linux host only. A small program printed
std::env::current_exe()andstd::fs::canonicalize. A symlink at/tmp/og-path-exp/bin/showexepointed at/tmp/og-path-exp/Cellar/omnigraph/0.14.1/bin/showexe.An extra symlink outside that tree resolved the same way. A copy of the program outside
Cellarresolved to its own path (cellar=false). On this host,current_exe()already returns the symlink target, because Linux exposes/proc/self/exe. That is not a macOS result. Homebrew's main bottle is macOS arm64. This host has no Homebrew cellar.A failing test that expects an update notice is not the right proof. The notice was never part of the contract.
version_command_prints_cli_version_http_contract_and_storage_formats_issue_977incrates/omnigraph-cli/tests/integration/cli_schema_config.rspins the currentversiontext. A notice on stdout would fail that test. A notice on stderr would not. The runs above are the user-visible proof.Claims
Confirmed. There are five install channels, and they update in different ways.
docs/user/install.mddocuments the shell installer, Homebrew, Nix, Windowsinstall.ps1, andinstall-source.sh. The container is documented there as an image and indocs/user/deployment.md. The image also contains the CLI: theDockerfilecopiesomnigraphto/usr/local/bin/omnigraph. A manualcargo buildis documented beside the source installer. npm packages are clients.docs/dev/versioning.mdsays current binaries ship through the installer, Homebrew, Docker, and GitHub Releases, and it says not to documentcargo install.What each channel does today:
scripts/install.shdownloads a GitHub release archive, checks the archive SHA-256, and copiesomnigraph,omnigraph-server, andomnigraph-azure-admissionintoINSTALL_DIR(default~/.local/bin). The default channel isstable, with a fallback toedge.RELEASE_CHANNEL=edgeandVERSION=<tag>select a channel. The script prints the channel. It does not store it.scripts/install.ps1does the same foromnigraph-windows-x86_64.zipinto%USERPROFILE%\.local\bin. Parameters are-ReleaseChannel,-Version, and-InstallDir.scripts/install-source.shbuilds the local checkout, or clonesSOURCE_REF(defaultmain), and installs the same three binaries. It records no ref.Formula/omnigraph.rbinModernRelay/homebrew-tapinstalls the three binaries withbin.installand points at thev0.14.0archives..github/workflows/release.ymlhas anupdate_homebrew_tapjob. The user command is the normalbrew upgrade ModernRelay/tap/omnigraph. This assessment did not runbrew.flake.nixbuilds the three release programs from source.install.mdshowsnix buildandnix run, and a pinnedgithub:ModernRelay/omnigraph/<ref>. It does not shownix profile upgrade.install.mdsayslatesttracks stable,edgetracks the rolling edge release, and a deploy should pin a digest. The entrypoint startsomnigraph-server. It does not update the CLI.Partly confirmed. The user can find the update command in
docs/user/install.md.The page tells the user how to install. It has no update section. A search of
docs/finds nobrew upgrade, nonix profile, and nodocker pull. Re-runninginstall.shis the same text as the install. The Homebrew, Nix, and Docker update commands are not written down.docs/user/operations/upgrade.mdis the storage-format procedure (omnigraph upgrade). It is not a binary update.Confirmed. Nothing in the product tells the user that a new release exists.
omnigraph versionis local.docs/user/install.mdsays it reports what the binary speaks, without opening a graph or contacting a server. There is no startup notice, noOMNIGRAPH_NO_UPDATE_CHECK, and no install receipt. Release pages exist on GitHub. The CLI does not point at them.Partly confirmed. The sample notice.
The lines in the issue (
0.15.0is available, you have0.14.1, HTTP contract0.14to0.15) are an example. They are not the live release state. GitHubreleases/latestisv0.14.0, published 2026-10-22. The package version in this tree is0.14.1(crates/omnigraph-cli/Cargo.toml). There is no0.15.0release. The rollingedgerelease currently holdsedge-20261103-03e0b62. Its body says storage format 16 and HTTP contract0.14, the same asv0.14.0. A correct notice today would not print a contract-change line for that pair.Confirmed, with a narrower close reason.
#79added a self-updater and was closed.#79is pull request #79, not an issue. Title: "feat(cli): add self-update mechanism (omnigraph update)". Closed 2026-05-12, not merged. It downloaded a release, verified SHA-256, and replacedomnigraphandomnigraph-serverwhen they were in the same directory. It also added a background stderr notice.The close comment from @aaltshuler says the shape does not fit the project. The stated blocker is the adjacent-server swap:
omnigraph updatereplaces the on-disk server binary while the process keeps the old file. The mismatch appears at the next restart, with no log line. The same comment records a smaller direction:omnigraph version --checkreads GitHub Releases and prints whether a newer release exists. Docs point CLI users atbrew upgradeor the install script, and server operators at their orchestrator. The comment says no self-replacement, no background notifier, and no hidden subcommand.The new proposal keeps the download-and-rename code out of the CLI. The script branch still runs
install.sh.install_from_dirinscripts/install.shcopiesomnigraph-serverinto the same directory. That is the same on-disk swap, done by the installer the CLI would start.Issue #11 proposed that self-updater. It is closed as completed (2026-04-12) and has no comments. The implementation never landed. Closing
#11does not mean the feature is onmain.Confirmed. The receipt, the path rules, and the Docker variable are absent.
No file under this SHA mentions
.omnigraph-install.json,OMNIGRAPH_INSTALL_METHOD, or/Cellar/omnigraph/. The onlyomnigraph-installstrings in the three scripts are temporary directory names. TheDockerfilesetsENV OMNIGRAPH_BIND=0.0.0.0:8080and no install method.docker/entrypoint.shstarts the server. It does not export an install method.Partly confirmed. Edge builds publish daily.
docs/user/install.mdsays edge builds are prereleases built once a day from the tip ofmain, after CI passes, whenmainhas moved. Recent tags match that pattern (edge-20261103-03e0b62,edge-20261103-36d3ea0,edge-20261101-3e0e467,edge-20261031-269299b). A quietmaindoes not get a build. A notice on every new edge tag would appear on almost every such day.Root cause
The CLI has one version report and one storage-upgrade command. They answer different questions.
VersionOutput::currentincrates/omnigraph-cli/src/commands/local/output.rscopies the compiled version,OMNIGRAPH_RELEASE_CHANNEL,OMNIGRAPH_SOURCE_VERSION,HTTP_API_CONTRACT("0.14"), and the storage stamps.write_versionprints those facts to stdout.maindoes not spawn a checker. The command enum incrates/omnigraph-cli/src/cli.rshasVersionandUpgrade.Upgradecallsomnigraph::db::upgrade_storage_as_with_storage. It converts a standalone graph at stamp 8, 9, or 13, or it stamps 14 or 15 up to 16. It does not replace a binary.The stamps are
INTERNAL_MANIFEST_SCHEMA_VERSION = 16andMIN_SUPPORTED_INTERNAL_SCHEMA_VERSION = 14incrates/omnigraph-catalog/src/migrations.rs.docs/dev/versioning.mdsays the HTTP contract must match exactly, and the CLI, the server, and the integrations move together. There is no old-client fallback.The release workflows already know these facts.
scripts/edge_release.pyreadsomnigraph version --jsonand writes the storage format and the HTTP contract into the edge release body, next to the latest stable release. The stablev0.14.0notes say in prose that the HTTP contract changed. Thev0.14.0assets are the four archives and their.sha256files. There is no JSON manifest. The GitHub "latest" API returns a tag and a markdown body. It has nohttp_contractfield and no storage-format field. Parsing that prose is a brittle contract.Two install trees also explain the receipt.
install.shandinstall-source.shboth write~/.local/bin. After that copy, the path does not say which script ran, which channel was selected, or which tag was pinned. Homebrew and Nix can be inferred from a resolved path. Docker cannot./usr/local/bin/omnigraphis also a normal copy.The name
updatecollides withupgrade. The CLI already points a typo forupdateat the storage conversion.Recommended fix
The user need is two questions. "Is there a release newer than this binary?" "What command updates the method that installed it, and must the server move too?"
The smallest design that answers both, and that does not start an installer:
Add an "Update" section to
docs/user/install.md. Give one command per channel. Say thatinstall.shandinstall.ps1also replaceomnigraph-serverandomnigraph-azure-admission. Say that a running server keeps the old file until the operator restarts it. Point server installs at the orchestrator, the image tag, or the package manager.Keep
omnigraph versionoffline, as the install guide promises. Addomnigraph version --check. The user asks for it. It performs one HTTPS GET of a small release file, then it prints the method's command. It writes no file. It starts no installer. It does not run onquery,mutate,cluster apply, or server boot. Exit 0 when the check completes, whether or not a newer release exists. A network failure is a message on stderr and a non-zero exit for that flag only.Publish
omnigraph-release.jsonas a release asset, generated fromomnigraph version --jsonin the release workflow. The edge workflow already runs that command. The file carries the tag, the package version, the channel,http_contract, andinternal_schema.writes/serves. The CLI compares those fields with its own compiled facts. It prints the contract line only whenhttp_contractorwritesdiffers. The GitHub Releases API remains the index of tags. It is the wrong source for the contract line. Do not parse release notes.For the shell installers only, write
.omnigraph-install.jsonbeside the binaries: method (scriptorsource), channel (stable,edge, or a pinned tag), and install directory. Trust it only when the resolvedcurrent_exe()is inside that directory and the recorded SHA-256 matches the running binary. Record the hash of each installed binary, not onlyomnigraph. A CLI and a server from different releases are the failure this notice exists to prevent. The fields are data. The file has no URL. The CLI must not pass the fields through a shell. A hash mismatch means "unknown". The receipt is not a signature. A person who can write the install directory can replace the binary and the receipt. That person can already replace the binary.Detection when there is no trusted receipt. Use the resolved
current_exe()path. On Linux,current_exe()already follows symlinks. Test path components, not a raw substring of an untrusted string, and confirm the macOS result before relying on/Cellar/omnigraph/. The Homebrew formula's prefix is the Cellar prefix. Also accept the Linuxbrew prefix. Nix: the resolved path is under/nix/store/. This was not executed in a store. Docker: do not use an environment variable as proof. The image can document the pull command. An unknown path prints that generic command. An env var is easy to set and easy to forge, and theDockerfiledoes not set one today.Edge. Do not notify on each edge build. Notify when the stable manifest is newer than this package version, or when its HTTP contract or storage stamp differs. A pinned
edge-<date>-<commit>install stays on that pin until the user asks.Do not add
omnigraph updateas a command that runsinstall.sh,install.ps1, orbrew upgrade. That command replaces the server binary beside the CLI. It is the behavior PR feat(cli): add self-update mechanism (omnigraph update) [MR-612] #79 closed to avoid. A print-only hint underversion --checkdoes not need a second verb.upgrademust stay the storage command.omnigraph-server, the container entrypoint, and cluster operations do not check for releases. A check inside the image would run from production hosts. Air-gapped and CI machines stay quiet because they never get a background check. An explicit--checkis the only network read, so a missing network cannot changequeryorcluster apply.Windows is a real channel (
install.ps1,omnigraph-windows-x86_64.zip). Any receipt and any printed command need a Windows test. This assessment did not run one.A
--checkJSON object would be a new CLI contract.docs/rfcs/README.mdasks for an RFC before a new CLI contract. Human text can land with the docs change. Stable JSON should wait for the RFC. Tests belong in the existingversionowner,cli_schema_config.rs. Path tests can use a temporary symlink, as in the experiment above. They must not call the live GitHub API.Alternatives and liability
A docs section alone removes the "which command?" gap. It adds no runtime behavior. It leaves "does a new release exist?" to the user.
A static line on
omnigraph versioncan repeat the install guide: this command does not look for a newer release, and the update commands are in the install guide. The line is local and permanent. It still does not know that a release exists.Package-manager text alone misses the curl installer. That installer is the first method in the README.
A background daily notice adds a network client, a cache, TTY and CI rules, and a second record of "what the latest release is". Review comments on PR #79 already had to chase cache identity and retry timing. The cache can be wrong, and every successful command pays for the process. The privacy cost is a GET from the user's network. The GET must not send a graph path, a token, or an actor. A static release file does not need the current version in the query. The command must still honor an HTTPS proxy and must give up on a short timeout. That behavior is justified only when the user asked for the check.
The full proposal (receipt, background notice, and
omnigraph updatethat runs the installer) removes the memory burden. It adds a receipt format and a command whose script path changes the server file. Five more changes of that kind become a second installer inside the CLI. The release asset andomnigraph version --jsonare already the source of the contract and the format. The check should read that source when asked, and it should print. It should not grow a writer.The trade is deliberate. The automatic notice is the part we drop. A database command must not contact the network, and it must not change a server file, unless the user asked for that operation. Convenience of a hint on every run is the lower priority.
Lance
Lance does not affect this issue. Omnigraph pins unmodified Lance 13.0.0 (
Cargo.toml,Cargo.lock). That tag is https://github.com/lance-format/lance/releases/tag/v13.0.0 (2026-10-07). Newer upstream tags include stable https://github.com/lance-format/lance/releases/tag/v14.0.2 (2026-10-29) and prerelease https://github.com/lance-format/lance/releases/tag/v15.0.0-beta.9 (2026-11-03). A search of openlance-format/lanceissues for a self-update, an update notifier, or an install receipt found nothing. The storage format in this proposal is Omnigraph's manifest stamp (16, serves 14 through 16), whichomnigraph versionalready prints.docs/dev/versioning.mdkeeps storage upgrades and the Lance analyzer boundary separate. A Lance bump would not tell a user how they installed Omnigraph. No Lance page is the matching domain for an install-method notice.Related work
omnigraph update) #11, closed as completed, implementation absent onmain: Add CLI self-update mechanism (omnigraph update) #11.docs/dev/versioning.md(exact HTTP contract, upgrade the CLI and the server together).main. An update hint must cite the release, not the docs onmain.install.mdalready says those docs can be newer than the stable binary.scripts/edge_release.pyis the existing generator of the contract and format comparison. A manifest should come from that path, not from a second hand-written table.Supersession
No open issue supersedes #1245. Issue #11 and PR #79 are the rejected self-replacing updater. They are closed. They are context. They are not a broader open tracker. This issue should stay open.
Limitations
macOS
current_exe()was not measured. Homebrew, Nix, Docker, and Windows were not executed. The install scripts were read in full and were not run. The debug binary was not rebuilt at this SHA. Its version output matches this tree. This comment does not change the repository.