Operate a customer-run pilot
This is the procedure qualified for a customer-operated pilot on one host. Every
step below runs in the Integration CI job pilot-lifecycle on a clean Ubuntu 24.04
container with pinned component versions. It is not a turnkey production release:
container images, a compose file, a privately managed mode and server-to-server
migration are not part of this pilot.
The operator tool is tools/pilot/pilot.py in the Integration repository; it keeps
one deployment directory with its configuration, owner-only secrets, process ids, the
gateway state (journal and payload store) and a journey record.
Install and sign in
Section titled “Install and sign in”Prerequisites: PostgreSQL you operate (an empty database and a login without a
password in the connection string — use peer authentication or a PGPASSFILE), the
pinned component checkouts, and the native arraybridge_server built from them.
python3 tools/pilot/pilot.py install --deployment /srv/ophiolite/pilot \ --dsn "host=db port=5432 user=ophiolite dbname=ophiolite" \ --components /srv/ophiolite/components --server /srv/ophiolite/bin/arraybridge_serverpython3 tools/pilot/pilot.py start --deployment /srv/ophiolite/pilotinstall applies the project schema and migrations in their documented order and
creates the pilot identities with the operator CLI (tools/project-admin.py): token
files are written owner-only and only their hashes enter the database. No Ophiolite
account, subscription or licence server is contacted. Browser sign-in through your own
identity provider is configured separately (see Deployment choices).
Upgrade
Section titled “Upgrade”python3 tools/pilot/pilot.py upgrade --deployment /srv/ophiolite/pilot \ --components /srv/ophiolite/components-next --archive /backups/before-upgrade.tar.gzThe sequence stops every process (native service, worker, gateway), takes a backup,
switches to the new pinned components (and binary, with --server), re-applies the
schema and migrations, starts and re-verifies every recorded exact revision and
refusal. Keep each release’s components in their own directory; do not check a new
release out over a running one. Downgrade is not supported: restore the
before-upgrade backup into a fresh database and directory instead.
Back up and restore
Section titled “Back up and restore”python3 tools/pilot/pilot.py stop --deployment /srv/ophiolite/pilotpython3 tools/pilot/pilot.py backup --deployment /srv/ophiolite/pilot --archive /backups/pilot.tar.gzpython3 tools/pilot/pilot.py restore --archive /backups/pilot.tar.gz --deployment /srv/ophiolite/restored \ --dsn "host=db2 port=5432 user=ophiolite dbname=ophiolite" --components /srv/ophiolite/components \ --server /srv/ophiolite/bin/arraybridge_server --secrets /srv/ophiolite/pilot/secretsA backup is taken while the deployment is stopped (a controlled write pause). It holds the project database dump, the gateway journal with every payload-store object it references (verified), and the source configuration, with a SHA-256 manifest. Restore checks the whole archive before touching anything (a changed, missing, extra, duplicated or unsafe member refuses), requires an empty target database and a new directory, then restores. If a restore fails part-way, drop that database and directory and run it again; the archive is never modified. Keep backups off the host.
Storage and limits
Section titled “Storage and limits”Retained bytes live in a content-addressed payload store. The default is a directory
beside the gateway journal; set OPHIOLITE_PAYLOAD_STORE to use another place:
OPHIOLITE_PAYLOAD_STORE=local:/srv/ophiolite/payloadsOPHIOLITE_PAYLOAD_STORE=s3:https://objects.example.com/ophiolite-bucket/pilotOPHIOLITE_PAYLOAD_STORE_CREDENTIALS=/srv/ophiolite/secrets/s3.json # {"access_key", "secret_key", "region"}, mode 0600An S3-compatible store keeps the same rules as the local one: objects are written once and verified on every read, one store serves one gateway journal, unreferenced objects are removed only by the offline sweep after a grace period. Credentials come only from the private file; bucket names and keys never appear in responses. A store that cannot be reached answers “busy, retry” (HTTP 503) rather than reporting damaged data. Backups copy the objects into local files; restore fills an empty store (an interrupted restore of the same backup resumes) before the journal is published.
Limits are per deployment: OPHIOLITE_MAX_UPLOAD_BYTES (default 8 MiB, at most
32 MiB) bounds one request and is enforced from the declared size before any upload byte is read;
OPHIOLITE_MAX_FILE_BYTES (default the same, at most 64 GiB) bounds a file sent in parts, and the
other limits of Large files have their own variables;
OPHIOLITE_SCIENTIFIC_READS (default 2) and OPHIOLITE_SCIENTIFIC_READ_WAIT_MS
(default 10000) set how many exact reads run at once and how long a request waits for a
turn before “busy, retry”. Exact reads accept one HTTP byte range and always carry the
whole representation’s SHA-256 in X-Content-SHA256.
Leave with your data
Section titled “Leave with your data”python3 tools/pilot/pilot.py exit --deployment /srv/ophiolite/pilot --output /exports/bundleexit exports the recorded exact revisions (well log, result, tops, survey, grid) as a
portable bundle through the Python SDK, stops the server, and reads and
recalculates them in a separate Python process with networking blocked. See
Take exact curves with you. A backup restores a
deployment; a bundle is how you use the data without one.
Move a project to another deployment
Section titled “Move a project to another deployment”A move copies one project’s originals and results — every exact version, sharing, review stages and endorsements, audience limits, result groups and run records — from a stopped deployment into another stopped deployment that has its own people. Asset ids, versions and bytes stay the same, so references written down before the move still name the same data.
python3 tools/pilot/pilot.py move --from /srv/ophiolite/pilot --to /srv/ophiolite/new --package /moves/pilotpython3 tools/pilot/pilot.py verify-move --from /srv/ophiolite/pilot --to /srv/ophiolite/newThe pilot tool uses the Platform command cli/migrate_project.py
(identity, export, import, reclaim), which you can also run directly:
- People are mapped explicitly. Every person named in the project must be mapped to an account that is a member of the destination project; two people may not map to one account. A person id found anywhere the tool does not map (for example a file name equal to someone’s id) stops the move instead of being rewritten.
- One deployment owns the project. A transfer is bound to the destination’s
identity (
migrate_project.py identity) and project; once the package is written the origin refuses every change to the project (HTTP 409authority-transferred) while reads and exports keep working, and the same package cannot be imported twice or elsewhere. A copy leaves the origin in charge; the destination refuses changes (409authority-copy). If a transfer package was never imported,reclaimwith that exact package returns the project to the origin. - What does not move: activity history (the destination records one “imported”
event with the package digest), inbox updates, linked sources and their drafts
(re-add the connections), reviewed releases and subscriptions, collections and native
scene assets. A project whose results were built on a reviewed release or a linked
source is refused before anything changes, and so is a project with a well or wellbore
associated with a retained copy of a source (a row or the whole of a captured table,
including associations since removed): the association would arrive without the copy
it names (from 2026.10.19; a package exported earlier from such a project lacks those
associations — do not import it;
reclaima transfer that was never imported). - A package names the release that made it (from 2026.10.20,
ophiolite-ops export-project). Importing one from before 2026.10.19, or one that does not name its release (the pilot tool’s packages, which run components rather than a release), completes and prints a warning: such a package may lack associations with retained copies of a source. Nothing in an older package tells whether it does, so every such package warns; ask the origin. Replayed sharing or upload commands from before the move are not honoured at the destination. - The authority check covers every change made through the gateway; the native service’s own scene editing is not gated.
Who is responsible for what (customer-operated mode)
Section titled “Who is responsible for what (customer-operated mode)”| Area | Operator (you) | Ophiolite software |
|---|---|---|
| Storage | Hosts the PostgreSQL database, the gateway journal and the payload store; can read all of them | Stores exact bytes content-addressed; never deletes during operation |
| Compute | Runs the native service, gateway and worker; Python calculations run on users’ computers | Records runs and results; no hosted execution |
| Access | Creates identities and project membership; configures the identity provider | Enforces project membership, per-asset grants, revocation and withdrawal |
| Operations | Backups, restores, upgrades, monitoring, TLS, network policy | Provides the pilot tool, migrations and verification |
| Exit | Chooses when to export | Portable bundles readable without the server |
| Telemetry | Disabled by default; an operator may configure an OpenTelemetry endpoint of their own | Sends nothing to Ophiolite |
What the operator can see: everything stored on the host — database rows (members, grants, activity), journal records (uploads, runs, receipts, sharing), payload bytes and logs. Users’ local Python work stays on their computers.
Release notes — 2026.10.83, any table read as it is
Section titled “Release notes — 2026.10.83, any table read as it is”Ophiolite 2026.10.83 reads a SQL table that is not a table of wells. See Read any table in Python.
What people see. Choosing a SQL table in Sources now asks how to read it: Wells table (as before) or Data table. A project administrator declares a data table once for the project: optionally a key, and per column a description, a unit and a missing-value marker, with a check of every row before it is saved. The declaration is the project’s; each selection is its selector’s own and is read with their own database sign-in; the review shows which columns are read and which are not.
What clients see. The SDK reads such a selection (profile table/1) into rows and a typed DataFrame, warns naming
any column that is not read, and refuses with require_all_columns=True. A read under a database sign-in that returns
other rows than the reviewed ones is refused (SOURCE_SIGN_IN_ROWS_DIFFER). Nothing is imported or kept: a data table is
read as it is. Well tables, their routes and their answers are unchanged; an earlier SDK lists a data table and refuses to
read it (source-not-supported).
Rollback. Going back to the previous release is the Upgrade guide’s restore from the backup taken before the upgrade; declarations and data-table selections saved after the upgrade are not in that backup.
Release notes — 2026.10.65, how a result was made
Section titled “Release notes — 2026.10.65, how a result was made”Ophiolite 2026.10.65 records how every calculated result was made and lets people make it again. See How a result was made.
What people see. A result shows who made it, through what, with which calculation and Ophiolite release, its settings and the exact version of each input; what changed between two versions; and what was made from it. Make it again runs the same calculation on the same or newer inputs, shows the result first and saves nothing unless the person chooses. The QGIS plug-in (0.10.0) opens a map’s history in the browser.
Release identity. The server image names its release in every calculation it records
(OPHIOLITE_RELEASE_FILE, written by the package). An image without it refuses to record a calculation rather than
record one it cannot place. Results made before this release read “How this was made was not recorded”.
Negotiated transition (revision manifest /3). A calculated result’s manifest is now version 3; it carries the
complete record of how it was made. It is served as /3 only to a reader that declares it (profiles on the descriptor
routes; this release’s SDK does). Every other reader, including every earlier SDK, gets the /2 view of the same
revision: the same values, the same top-level method, and a /2 digest that verifies as before. Nothing already stored is
rewritten. Migration, for a script written against /2: install this release’s SDK, which declares /3 on every
descriptor read and verifies it, and read the record through the story rather than the manifest:
page = client.story(asset_id, revision) # client: an ophiolite.Client of this releaseroot = next(node for node in page['nodes'] if node['id'] == page['root'])print(root['kind'], root['method'], root['settings'])An earlier SDK keeps working unchanged; it reads, verifies and exports the /2 view.
Rollback. Going back to the previous release is the Upgrade guide’s restore from the backup taken before the upgrade; results saved by Make it again after the upgrade are not in that backup.
Release notes — 2026.10.1, the transition release
Section titled “Release notes — 2026.10.1, the transition release”Ophiolite 2026.10.1 moves the shared-asset catalog, retained external snapshots, original files, and reviews into the managed store. It also makes conditional sharing mandatory. This is a breaking transition for clients using the retired native catalog API, including the archived desktop Workbench.
Sharing. Recipient changes must include the sharing version previously read.
The current Workspace and SDK send it. Custom SDK calls must provide
expected_generation; requests without it return HTTP 428 condition-required
and do not change permissions. A stale version is refused rather than overwriting
someone else’s changes. The old switch cannot enable unconditional sharing.
Catalog and history. The upgrade copies and verifies the migrated records before the gateway starts. Supported catalog routes continue resolving migrated legacy asset identifiers and revision numbers. Registration history retains historical bytes only where they can be established; unavailable historical bytes are explicitly marked not retained, never replaced by the current data. This does not add portable-bundle support for every native registration type; the gateway-backed legacy exporter remains available for that scope.
Client changes. The supported gateway catalog and review routes replace the retired
native gRPC methods. Update connector runtimes and client packages to this release’s
pinned versions. Configure connector runtimes with the Workspace gateway address
and a project-bound credential. The legacy exporter now needs --gateway and its
project-bound credential.
The QGIS plugin supports scalar maps. Its browser sign-in/read path is not a replacement for Workbench curves, tops, or every publication workflow. QGIS publication requires its supported publication authentication/configuration; the browser OAuth connector credential alone does not enable the imports routes.
Archived desktop Workbench. Against 2026.10.1, the retired native calls can no longer list/open shared catalog assets, publish external snapshots, or read/write review scenes and comments. Use supported Workspace and SDK workflows, and QGIS for supported scalar-map operations. The underlying numerical, run and workspace APIs are not retired by this change; that is not a claim that every Workbench workflow still works.
Compatibility window. The replaced HTTP gateway routes remain available as
compatibility aliases, with deprecation headers and a successor link. Their advertised
sunset is 31 March 2027. This window applies to the gateway aliases, not to the
native gRPC calls, which return UNIMPLEMENTED in 2026.10.1. ImportExternalAssets
has no direct replacement; asset_bridge_sdk.import_assets raises ImportRetired.
| Existing path/function | Replacement under /api/v1/projects/{project}/ |
|---|---|
| Asset list, get, history and changes | catalog/list, catalog/get, catalog/history, catalog/changes |
| External snapshots and originals | catalog/snapshot, catalog/original |
| Review scenes and comments | reviews/scene-list, reviews/scene-get, reviews/scene-save, reviews/comment-list, reviews/comment-add |
| Project membership through the gateway | members/list, members/set |
| External source registration and publication | imports/source, imports/original-chunk, imports/snapshot |
| Native asset watching | maps/watch |
Packaged operation. The release adds operator configuration for browser/application sign-in, explicit provider-to-user identity links, approved sources and connection administration. Browser and SDK access require that identity configuration; a bare token-only installation does not provide those flows. The evaluation identity provider is for local testing. Operator setup and recovery details are in the release’s Install and Recovery guides.
The operator migration steps are in Upgrade.
Release notes — pilot release 2026-09-27
Section titled “Release notes — pilot release 2026-09-27”- Sharing cutover announced. Replacing recipients without the version you saw
(unconditional sharing) is deprecated. This release adds the switch
OPHIOLITE_REQUIRE_CONDITIONAL_SHARING=1, which refuses it (HTTP 428,condition-required) and changes nothing. It is off by default in this release and will be on by default in the next release. Workspace and the current SDK already share conditionally. - New since the previous pilot release: result versions, the local payload store, portable bundles, well tops/trajectories/horizon grids, connector capability pages and explicit updates for locked sources.
Not covered by this pilot
Section titled “Not covered by this pilot”Container images and compose, systemd activation with install-server.py --apply,
Caddy TLS and Keycloak/browser sign-in in the automated job, ARM64, privately managed
mode, online (running) project moves, public build inputs and
licence files (E17), and the matching source/notices package.
