Skip to content

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.

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.

Terminal window
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_server
python3 tools/pilot/pilot.py start --deployment /srv/ophiolite/pilot

install 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).

Terminal window
python3 tools/pilot/pilot.py upgrade --deployment /srv/ophiolite/pilot \
--components /srv/ophiolite/components-next --archive /backups/before-upgrade.tar.gz

The 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.

Terminal window
python3 tools/pilot/pilot.py stop --deployment /srv/ophiolite/pilot
python3 tools/pilot/pilot.py backup --deployment /srv/ophiolite/pilot --archive /backups/pilot.tar.gz
python3 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/secrets

A 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.

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:

Terminal window
OPHIOLITE_PAYLOAD_STORE=local:/srv/ophiolite/payloads
OPHIOLITE_PAYLOAD_STORE=s3:https://objects.example.com/ophiolite-bucket/pilot
OPHIOLITE_PAYLOAD_STORE_CREDENTIALS=/srv/ophiolite/secrets/s3.json # {"access_key", "secret_key", "region"}, mode 0600

An 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.

Terminal window
python3 tools/pilot/pilot.py exit --deployment /srv/ophiolite/pilot --output /exports/bundle

exit 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.

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.

Terminal window
python3 tools/pilot/pilot.py move --from /srv/ophiolite/pilot --to /srv/ophiolite/new --package /moves/pilot
python3 tools/pilot/pilot.py verify-move --from /srv/ophiolite/pilot --to /srv/ophiolite/new

The 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 409 authority-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 (409 authority-copy). If a transfer package was never imported, reclaim with 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; reclaim a 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)”
AreaOperator (you)Ophiolite software
StorageHosts the PostgreSQL database, the gateway journal and the payload store; can read all of themStores exact bytes content-addressed; never deletes during operation
ComputeRuns the native service, gateway and worker; Python calculations run on users’ computersRecords runs and results; no hosted execution
AccessCreates identities and project membership; configures the identity providerEnforces project membership, per-asset grants, revocation and withdrawal
OperationsBackups, restores, upgrades, monitoring, TLS, network policyProvides the pilot tool, migrations and verification
ExitChooses when to exportPortable bundles readable without the server
TelemetryDisabled by default; an operator may configure an OpenTelemetry endpoint of their ownSends 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 release
root = 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/functionReplacement under /api/v1/projects/{project}/
Asset list, get, history and changescatalog/list, catalog/get, catalog/history, catalog/changes
External snapshots and originalscatalog/snapshot, catalog/original
Review scenes and commentsreviews/scene-list, reviews/scene-get, reviews/scene-save, reviews/comment-list, reviews/comment-add
Project membership through the gatewaymembers/list, members/set
External source registration and publicationimports/source, imports/original-chunk, imports/snapshot
Native asset watchingmaps/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.

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.