Skip to content

Python and TypeScript SDK

Reviewed 26 September 2026. The supported 0.x SDK reads and publishes bounded scientific curve revisions through the public API. Contracts remain preview contracts. Source installation is qualified on Python 3.10/3.12; stable 1.0, PyPI/npm publication, a complete open-source release and production support are separate release gates.

Follow Read data in your own Python for browser login and a complete read (login uses the deployment’s identity provider; see Install › Sign-in). Use a private virtual environment:

Terminal window
python -m pip install 'ophiolite[pandas,numpy,validation] @ git+https://github.com/tuna-soup/ophiolite-sdk@ac62ad00e2fe399756474cd655ee6b750966dd88'
ophiolite skills path

This installs the Ophiolite SDK of release 2026.11.18.

Exact versionac62ad00e2fe399756474cd655ee6b750966dd88

The installed guides cover authentication, exact reads, local validation, publication, sharing and recovery. They use public APIs and confer no new rights. Source and API examples include sync Client, async AsyncClient, typed descriptors and bounded local validation. Validation and contract imports need no server or credential cache.

client.assets() discovers permitted assets. client.read(asset, revision, ["GR", "RHOB"]) selects exact curves explicitly. These names are an example; choose curves returned for your asset. Reads require no binding or run.

data.to_frame() returns a DataFrame and a separate descriptor; data.to_numpy() returns an array and its descriptor. Preserve both together. Metadata is not silently attached: descriptor.attach(frame) is explicit. Missing samples become NaN in numerical views; normalized JSON uses null and zero stays zero. Original LAS bytes preserve their declared NULL marker.

The descriptor preserves source identity, units, axis/depth reference, interpretation and checksums. Multi-curve views require matching axes and scientific context. No unit conversion, interpolation, CRS/datum inference or well identity matching happens automatically. Unknown units remain unknown. Source revisions are opaque; do not generalize a managed-derived checksum rule to all sources. Changed mappings refuse; reader-version differences stay explicit.

data.workspace_url() opens the selected scientific revision and curve in Workspace. Access and retention still apply to old revisions. Synthetic templates show this URL contract but do not serve Workspace themselves.

Use client.work_folder(private_folder) for configure/start, local calculation, validation, publication and download. A run-start records/resolves input; it does not execute Python. Inspect your result before explicit publication. Current publication creates a separate derived asset; it does not append to its parent. Sharing is a separate authorized operation.

Preserve the entire private folder. client.recover(private_folder) replays the saved exact request and validates its response; a completed folder returns None. Direct calls without a work folder do not provide recovery after process exit. A command ID alone is insufficient. Changed inputs/code need a new folder. Recovery binds the gateway, project and identity. A fresh approved grant for the same confirmed user is supported; an opaque delegate requires the original fingerprint. Credentials are not stored in the folder. This is process-restart qualification, not a power-loss guarantee.

OperationRetry and recovery
Reads and inspectionUp to three attempts for transient transport/busy responses
Configure, start, upload and publishSame command/body on bounded retries; work folders persist the exact request
SharingNever automatic; after ShareOutcomeUnknown, read client.grants(receipt) first and decide from current state
Login, refresh and revokeNever replay an ambiguous request automatically
ErrorMeaning and next step
AuthenticationRequired / PermissionRefusedRenew authorized access or ask the owner for the required grant; no credential fallback
VerificationFailed / InterpretationChangedBytes or interpretation cannot be trusted; inspect exact artifacts and reader version
AxisMismatch / IncompatibleRead separately or choose and document an explicit supported transformation
ValidationFailedCorrect reported output violations before publication
IntegrityConflictSaved command and request disagree; preserve evidence, do not invent a new command to bypass uncertainty
CapacityExceeded / BusyReduce the bounded request or follow the reported retry guidance
RecoveryUnavailablePreserve the original folder and evidence; arbitrary disk-loss recovery is not promised
ShareOutcomeUnknownRead current grants before deciding whether another sharing action is needed

ophiolite.agents.AgentClient(base_url, project, credential) uses an agent credential from Settings → Agents: read() for reads, propose(), wait() and execute() (or run() for all three) for changes, recover(command_id=… | plan=…) after a lost answer.

A job is 1 to 8 steps under one approval (ophiolite.agent-job/1):

MethodWhat it does
propose_job(steps, *, evidence=None, files=None)Posts jobs/propose with steps {operation, body}. Each body is copied and names the client’s project (project_id is added when absent). A body value {"$step": k, "pointer": "/…"} is part of step k’s answer. A malformed list or a step naming another project raises Refused (a ValueError) before any request. Which operations a job may run is the gateway’s decision, in its words. files is {step: bytes} for each file step (table-imports/read): its body gains the file’s bytes and sha256, and a file step without its bytes, or naming other bytes, raises Refused before any request
job_status(plan)Posts jobs/status; the answer is checked against ophiolite.agent-job/1 without the validation extra (VerificationFailed otherwise)
step(plan, step, *, evidence_id=None, command_id=None)Posts jobs/step. The command id defaults to uuid5(NAMESPACE, f'{plan}:{step}'), with ophiolite.agents.NAMESPACE a fixed published UUID, so every process sends the same id; a completed step answers its recorded answer and writes nothing
run_job(steps, *, evidence=None, files=None, **wait)propose_job, wait for the approval (the evidence id as for run), then every step in order; returns the answers. A file step’s bytes go to its own route (send_file_step)
resume(plan, *, files=None, **wait)Waits while the plan is pending, replays completed steps by their ids and runs the rest; returns every answer in order. A file step that has not run needs its bytes in files and its body from this process’s propose_job; after a restart, call run_job with the same steps and files (the same plan)
send_file_step(plan, step, operation, body, raw, *, evidence_id=None, command_id=None)Posts the file to the step’s own route with the approved body in X-Ophiolite-Upload, X-Ophiolite-Plan and X-Ophiolite-Job-Step: <step> <command_id> (the id step uses); a completed file step answers its recorded answer

A step that needs the person’s decision raises ChoiceRequired with its question, choices, choice_token, plan and step. A declined or expired job raises PlanDeclined or PlanExpired; a failed job raises the gateway’s word (BudgetExhausted, CommandOwned, or Refused with that code). CommandOwned from a completed step whose answer was never stored is not recoverable through the SDK. job(job_id) stays the runner-job reader (E13).

The person’s own Client has proposals() (agents/proposals, the envelope checked against ophiolite.agent-proposals/1): every pending plan and every job waiting for their choice. It also has answer(choice_token, choice) (jobs/answer), which returns {plan, step, state: 'running', choice}. An agent credential is refused on both by the gateway (agent-refused, 403). A used, foreign or stale token, or another person’s job, raises ChoiceRefused (choice-refused) with details['reason'] one of token-used, token-foreign, token-stale or not-the-delegator.

See Let an agent work for you.

Four starter sources provide an agent workflow, executed notebook, Python application and React app. Each records its template/SDK version, locked dependencies, synthetic fixtures, README and explicit test command. Synthetic mode needs no credentials; live mode uses your authorized configuration. The notebook preserves descriptors alongside numerical data; the React starter keeps credentials in its local Python backend. Its loopback server requires explicit browser origin and per-start request proof. Fixture tests establish bounded examples, not production deployment or connectors.

client.grants(asset) returns the recipients and their generation (None when the server predates conditional sharing). Share with client.share(asset, read=[...], reuse=[...], expected_generation=grants.generation): after a lost response the SDK repeats the identical request once, and that repeat raises IntegrityConflict instead of undoing a change made meanwhile. Without expected_generation the call is unconditional, never repeated, and warns as deprecated. ophiolite share reads the current generation first. See Share data with colleagues.

client.publish(run, changes=..., new_version_of=previous_receipt) (also on work folders) adds the next version of your own result (or of a colleague’s, when the project names you both as members who add versions to each other’s results), with the revision you reviewed as expected parent; if someone added a newer version first the SDK raises IntegrityConflict and nothing changes. client.history(result) lists every version you can read of a result or a derived publication, including those someone else published and shared with you — pass the asset as {"asset_id": ..., "revision": ..., "authority": ...} from client.assets(). The versions of an uploaded original are not listed: history refuses one in plain words; read the exact revision you were given, or ask its uploader (2026.10.22).

client.read_data(asset, revision) returns WellTops, Trajectory or GridSurface with the exact original, verified descriptor and data (to_frame(), to_numpy(), cell_center(), explicit minimum_curvature()); also on AsyncClient. Export them with None as the curve list. See Well tops, trajectories and horizon grids.

read_data also returns PolylineSet (fault sticks in file order, to_frame()) and SeismicVolume — the volume’s description (grid, sample meaning, every header decision, per-inline chunk digests); the volume itself is not downloaded. client.read_slice(asset, revision, axis, label) returns a SeismicSlice (values, rows, columns, to_numpy(), value_at(), scope) checked against that description; axis is 'inline', 'crossline' or 'sample' and label the line number or the 0-based sample number. client.export_slices(asset, revision, [(axis, label), ...], folder) writes a bundle 2.3 with the chosen slices and names the original by its digest. Both are also on AsyncClient. See Fault sticks and seismic volumes.

client.export([(asset, revision, curves), ...], folder) writes a new portable bundle of exact revisions (all or nothing, never overwriting); ophiolite.bundle.open_bundle(folder) checks and loads it offline, and plot_svg(curve, path) draws a track. The CLI offers ophiolite export and ophiolite bundle check|show. See Take exact curves with you.

@ophiolite/client provides generated operations and verified readCurve. Build from the reviewed source; npm publication is pending. Host applications own credentials and refresh. Session mode uses Workspace CSRF/cookies; bearer mode uses an explicit supplier and omits browser cookies. Redirects refuse. Calls make one HTTP attempt; sharing never retries. There is no browser work-folder journal. Incomplete server response schemas remain unknown or documented adapters; a generated request alone does not prove scientific integrity.

Python and TypeScript share 0.MINOR.PATCH and one release tag. PATCH fixes defects or adds APIs. MINOR may break APIs with a CHANGELOG Breaking entry and a one-minor shim where feasible. A new schema suffix requires a new model/MINOR. A stable 1.0 promise requires supported registry entries for every SDK read and stable OpenAPI.

The old pilot 0.2 modules remain in the 0.3.0 compatibility kit for one announced transition release. Fresh SDK login is required; never copy old credential caches. They are removed in the first versioned distribution release afterward. The SDK, TypeScript client, templates, guides and extracted contracts are Apache-2.0; server licensing and full release qualification are separate.