Ocean CLI is the command-line client for Ocean Protocol. It talks to an Ocean Node (which handles indexing, metadata caching, encryption, ordering and compute) and to an EVM chain through an RPC endpoint. With it you can:
- Publish and edit data assets: downloadable files or compute-to-data.
- Consume assets: order datatokens and download data.
- Run compute-to-data (free or paid) on published datasets with a published or raw algorithm.
- Run on-demand services: long-running containers (JupyterLab, inference servers, ...) on compute environments, paid via escrow.
- Manage escrow payments, access lists, persistent-storage buckets and auth tokens.
Built on @oceanprotocol/lib (ocean.js ^9.0.0) and @oceanprotocol/ddo-js (^1.0.0). Problems? Open a new issue.
Requirements | Install | Quick start | Configuration | How the CLI works | Nodes and chains | Command guide | Command reference | Sample metadata | Troubleshooting | Development | License
- Node.js
>=22(download) - Linux, macOS, or Windows via WSL
- An RPC endpoint for the chain you target
- A funded wallet (native gas token, plus datatokens / payment tokens as needed)
- An Ocean Node to talk to (HTTP(S) URL, libp2p peer id, or multiaddr)
npm install -g @oceanprotocol/cli
ocean-cli --version
ocean-cli h # list all commands--help, -h, --version and h work with no environment variables set. Every other command needs the configuration below. Contributors: see Development.
export PRIVATE_KEY="0x..." # or MNEMONIC="word1 word2 ..." export RPC="https://your-rpc.example" export NODE_URL="http://127.0.0.1:8001" # Publish a sample asset (edit chainId / serviceEndpoint in the file first) ocean-cli publish metadata/simpleDownloadDataset.json # Download it (prints the DID on publish) ocean-cli download did:op:123 ./downloads # See where you can run compute ocean-cli getComputeEnvironments
All configuration is via environment variables.
Required
| Variable | Description |
|---|---|
PRIVATE_KEY or MNEMONIC |
Signer credentials. The private key is preferred. |
RPC |
Either a single URL (http://localhost:8545) or a JSON map keyed by chainId whose values are a URL or an ordered list of URLs: '{"1":"https://eth.example","8453":["https://a","https://b"]}'. Two or more URLs for a chain become an ethers v6 FallbackProvider (quorum: 1, declaration order = preference). A malformed value fails at startup with the expected shape. |
Recommended
| Variable | Description |
|---|---|
NODE_URL |
The initial Ocean Node: HTTP(S) URL, libp2p peer id, or full multiaddr. The CLI starts without it, but then only setNode, setNodeEnv, getNode, help, searchComputeResources and the chain commands work; everything else fails with No Ocean Node set until you run setNode. (searchComputeResources runs node-free but still needs RPC, a signer and libp2p — it is a way to find a node before selecting one.) In practice, set it. |
Optional
| Variable | Default | Description |
|---|---|---|
CHAIN_ID |
resolved automatically | Default (active) chain for the session. See Nodes and chains. |
RPC_CONFIG_FILE |
~/.ocean/cli/rpc.json |
Where runtime-added chains are persisted (same JSON-map shape, hand-editable). Env RPC is merged with it and env wins on conflict. |
ADDRESS_FILE |
ocean.js bundled addresses | Path to a contracts address.json for Barge / custom deployments. Without it, escrow, access-list and Ocean-token addresses come from the multi-chain set bundled with ocean.js. |
DISABLE_P2P |
unset | true skips libp2p. Fatal at startup if combined with a P2P NODE_URL (peer id / multiaddr). Use it only if you exclusively use HTTP nodes. searchComputeResources needs libp2p. |
BOOTSTRAP_PEERS |
unset | Comma-separated extra libp2p multiaddrs, added to the built-in Ocean bootstrap list. |
AVOID_LOOP_RUN |
unset | true = one-shot: run the command and exit (use for CI and scripts). Unset = interactive. |
INDEXING_MAX_RETRIES |
120 |
Max retries while waiting for an asset to be indexed. |
INDEXING_RETRY_INTERVAL |
4000 |
Milliseconds between indexing retries. |
SSI_WALLET_API, SSI_WALLET_ID, SSI_WALLET_DID |
unset | SSI wallet settings for v5 DDOs (credentialSubject / SSI policy flows). |
mintOcean additionally needs an Ocean token address, from the chain's configured oceanTokenAddress or --token <address> (some chains, e.g. Base, ship none).
- Interactive vs one-shot. By default the CLI runs the command you pass (if any) and then stays in a prompt (REPL). Leave with
exit,quit, ESC or CTRL-C. In the prompt you can type the bare command or paste a fullocean-cli ...line. WithAVOID_LOOP_RUN=trueit runs once and exits. In one-shot modesetNodeonly validates; the switch dies with the process, so useNODE_URL. - Positional and named arguments. Named options may come in any order and override the matching positional when both are given. But a required positional (
<x>) must still be supplied positionally — passing only its named option fails with Commander's missing required argument (e.g.publish --file metadata.jsonerrors; usepublish metadata.json, andpublish metadata.json --file other.jsonlets the option win). Optional positionals ([x]) may be omitted or supplied via their named option. A few commands (e.g.downloadJobResults) are positional-only. - JSON arguments. Single-quote JSON on the shell and put
--before the arguments so option parsing stops:ocean-cli startCompute -- '["did:op:a",{"fileObject":{...}}]' did:op:algo .... - Help.
ocean-cli horocean-cli helpprints the grouped command list (thehelpcommand takes no argument). For a specific command's arguments and options, add--help/-hafter it:ocean-cli publish --help.
Choosing the node (works inside the interactive prompt, no restart):
ocean-cli setNode http://127.0.0.1:8001 # alias useNode; also a peer id, multiaddr, or --node ocean-cli getNode # alias currentNode: active node, version, chains served ocean-cli setNodeEnv '16Uiu2HAm...|0xff10...-0xc92e...' # alias useNodeEnv (quote: the token contains a '|')
setNode health-checks the node first; if unreachable, nothing changes. setNodeEnv takes the Node+env: token printed by searchComputeResources, switches node like setNode and remembers the compute env so startCompute / startFreeCompute need no --env. Notes:
- Compute jobs live on the node that started them; after a switch,
getJobStatus/downloadJobResultsquery the new node. - For a node on your own machine prefer the full multiaddr (
/ip4/127.0.0.1/tcp/9001/ws/p2p/<peerId>); a bare peer id needs DHT lookup, which may not advertise localhost. chainIdalways comes fromRPC, never from the node.setNodewarns if the node does not serve your chain;getNodeflags served chains with no RPC configured.
Managing chains (node-free; work before a node is set):
ocean-cli addChain 137 https://polygon-rpc.com [https://backup...] # alias addRpc; each URL is verified ocean-cli removeChain 137 # alias removeRpc ocean-cli listChains # aliases getRpcs, chains ocean-cli setChain 137 # alias useChain; persisted default ocean-cli getChain # alias currentChain
Added chains persist to ~/.ocean/cli/rpc.json. The active default chain is: setChain / CHAIN_ID -> persisted default -> the only configured chain. If none of those resolve, commands that target a specific chain require an explicit --chainId. (The display-only listChains additionally reports the single chain the node and your registry both know, but that intersection is not used to auto-select a chain for commands.)
How each command picks its chain:
| Category | Commands | Chain used |
|---|---|---|
| Chain-agnostic | reads, jobs, storage, auth tokens, node/chain management | Default (or any registered) chain; node/chain management does not sign |
| DDO-implied | publish, publishAlgo, editAsset, allowAlgo, download |
The DDO's own chainId |
| Explicit | mintOcean, all escrow, all access-list, startService, extendService, startCompute, startFreeCompute |
--chainId <id>, else the default chain, else an error listing configured chains |
For startCompute, --chainId is the payment/escrow chain only; each asset is ordered on its own DDO chain, so one job can span chains. Every chain involved must be a registered RPC. For startFreeCompute it only selects the signing chain.
Each section shows the common usage; run ocean-cli <command> --help for every option, or see the reference table.
ocean-cli getDDO did:op:123 # or --did ocean-cli publish metadata.json # or --file; add --encrypt false to skip DDO encryption ocean-cli publishAlgo algorithm.json # same options as publish ocean-cli editAsset did:op:123 metadata.json # alias edit; or --did / --file ocean-cli download did:op:123 ./custom-folder [serviceId] # or --did --folder --service ocean-cli allowAlgo did:op:dataset did:op:algo # dataset NFT owner only; --encrypt true|false
download defaults to services[0]; a serviceId not present in the DDO fails fast. For v5 DDOs (version >= 5.0.0) it first runs a provider-initialize step, and with SSI_WALLET_API set it performs the SSI / policy-server verification (skipped automatically when the node has no policy server).
ocean-cli getComputeEnvironments [nodeUrlOrPeerId] # alias getC2DEnvs; prints a payment summary per env ocean-cli searchComputeResources --cpu 4 --ram 16 --paid --chain 8996 --order-by price # alias findComputeNodes ocean-cli startCompute --accept true -- did1,did2 algoDid env1 3600 0xPaymentToken '[{"id":"cpu","amount":1}]' ocean-cli startFreeCompute did1,did2 algoDid env1 ocean-cli getJobStatus did:op:123 job-123 # or --dataset --job [--agreement] ocean-cli downloadJobResults job-123 0 ./results # positional only (jobId, index, folder) ocean-cli stopCompute did:op:123 job-123 <agreementId> # agreementId is required ocean-cli computeStreamableLogs <jobId> # or --job
startCompute details:
maxJobDuration(seconds) is required and drives the payment.paymentTokenis required: a token the environment accepts on the payment chain (seegetComputeEnvironments).resourcesis required: stringified JSON within the environment's limits, e.g.'[{"id":"cpu","amount":3},{"id":"ram","amount":16772672536},{"id":"disk","amount":0}]'.--accept trueauto-confirms payment (mandatory when stdin is not a TTY);falseprompts.--services(comma-separated dataset service ids, mapped by position to the datasets) and--algo-service(the algorithm's service id) are optional; the first available service is used when omitted.--outputis an optional stringified JSON remote-storage backend for results (S3, FTP, URL, Arweave, IPFS); omitted means results stay on the node's disk. Example:'{"remoteStorage":{"type":"s3","s3Access":{"endpoint":"https://s3.amazonaws.com","region":"us-east-1","bucket":"my-results","objectKey":"jobs/result.tar","accessKeyId":"AKIA...","secretAccessKey":"..."}}}'.
startFreeCompute takes the same dataset/algo/--services/--algo-service/--output arguments, targets a free environment and does no ordering or payment.
Raw (unpublished) datasets and algorithms. Instead of a DID, pass a full ComputeAsset (datasets) or ComputeAlgorithm JSON object with a fileObject. Raw entries have no DID and no datatoken order, and can be mixed with DIDs (the value must then be valid JSON, DIDs quoted). Works for both startCompute and startFreeCompute:
# Raw algorithm on a dataset that sets allowRawAlgorithm: true ocean-cli startCompute --accept true -- did:op:dataset '{"fileObject":{"type":"url","url":"https://example.com/algo.py","method":"GET"},"meta":{"container":{"entrypoint":"python $ALGO","image":"oceanprotocol/algo_dockers","tag":"python-branin","checksum":"sha256:..."}}}' env1 900 paymentToken resources # Raw dataset on a published algorithm ocean-cli startCompute --accept true -- '[{"fileObject":{"type":"url","url":"https://example.com/data.csv","method":"GET"}}]' did:op:algo env1 900 paymentToken resources # Mixed: published DID plus raw IPFS file ocean-cli startCompute --accept true -- '["did:op:dataset",{"fileObject":{"type":"ipfs","hash":"Qm..."}}]' did:op:algo env1 900 paymentToken resources
Finding a node. searchComputeResources is a P2P/DHT search (needs libp2p, so not with DISABLE_P2P=true; works before setNode). Run it with no filters for an interactive wizard, or script it with flags: --cpu --ram --disk --gpu --gpu-model --resource name:amount (repeatable) --free|--paid|--both --chain <ids> --token <addrs> --max-price --duration --order-by price|freeCapacity|resources|leastBusy. --chain and --token take comma-separated lists; a paid env pricing on several requested chains is listed once per chain. Each match starts with a ready-to-run setNodeEnv <node>|<env> line. Environments that fall short of the request are filtered out, with a note on how many.
An on-demand service is a long-running container that stays up until it expires, is stopped, or is extended, reachable at http://<nodeHost>:<hostPort>. Escrow must be funded and authorized first.
ocean-cli getServiceTemplates # alias serviceTemplates # Fund escrow and authorize the env's consumer address (maxLockSeconds >= duration + 3600) ocean-cli depositEscrow 0x<token> 100 ocean-cli authorizeEscrow 0x<token> 0x<envConsumerAddress> 100 90000 100 # From an operator template, or your own image (tag goes in --tag, not --image) ocean-cli startService <envId> 3600 0x<token> --template jupyter-cpu --user-data '{"JUPYTER_TOKEN":"secret"}' ocean-cli startService <envId> 3600 0x<token> --image nginxinc/nginx-unprivileged --tag alpine --ports 8080 ocean-cli getServiceStatus <serviceId> # alias myServices; YOUR services, full detail ocean-cli getServices --status 40 # alias listServices; ALL owners on the node ocean-cli serviceLogs <serviceId> --since 10m # alias computeServiceLogs ocean-cli extendService <serviceId> 1800 --accept true ocean-cli restartService <serviceId> # reuse stored spec ocean-cli restartService <serviceId> --image myrepo/algo --tag v2 # rebuild on a new image ocean-cli stopService <serviceId>
- Duration is in seconds. The CLI prints an estimated cost; the authoritative cost is shown as
Node-computed cost:after start. - Start is asynchronous: it returns a
serviceIdin statusStarting (10), then the CLI polls untilRunning (40)(unless--wait false;--timeoutdefaults to 600 s). If interrupted, resume withgetServiceStatus <serviceId>. - Escrow shortfalls do not fail the HTTP call; they surface as the job ending in
Error. The CLI pre-verifies funds and authorization and aborts with the exact remediation commands. - Image spec: at most one of
--tag,--checksum,--dockerfile. Keep the tag out of--image, otherwise the node builds an invalidimage:tag:latest. - Containers run with
CapDrop ALLand cannot bind ports below 1024; listen on a high port such as 8080. --user-datais a JSON object of container env vars, encrypted to the node's key. Values are never logged.getServiceStatusshows your services in full;getServiceslists all owners' services with the image spec stripped and supports--status,--include-all,--from.restartServicewith no spec flags bounces the container unchanged (REUSE). Any of--image --tag --checksum --dockerfile --additional-docker-filesrebuilds it on the new spec (RESPEC), keeping ports, expiry and payment window at no extra charge.--cmd/--entrypointreplace the stored command (empty array clears).
ocean-cli depositEscrow 0xToken 100 # or --token --amount ocean-cli getUserFundsEscrow 0xToken ocean-cli withdrawFromEscrow 0xToken 50 ocean-cli authorizeEscrow 0xToken 0xPayee 1000 3600 10 # token payee maxLockedAmount maxLockSeconds maxLockCounts ocean-cli getAuthorizationsEscrow 0xToken 0xPayee
All accept --chainId. authorizeEscrow flags: --token --payee --maxLockedAmount --maxLockSeconds --maxLockCounts.
ocean-cli createAccessList "My Access List" "MAL" "0xUser1,0xUser2" false # name symbol [initialUsers] [transferable, default false] ocean-cli addToAccessList 0xListAddress "0xUser1,0xUser2" ocean-cli checkAccessList 0xListAddress "0xUser1,0xUser2" ocean-cli removeFromAccessList 0xListAddress "0xUser1,0xUser2"
Named forms: --name --symbol --initial-users --transferable, and --address --users. All accept --chainId.
ocean-cli createBucket [accessListAddress] # omit for owner-only access ocean-cli addFileToBucket <bucketId> ./file.csv [fileName] # name defaults to the file's basename ocean-cli listBuckets [--owner 0xOwner] # defaults to the signer ocean-cli listFilesInBucket <bucketId> ocean-cli getFileObject <bucketId> <fileName> # file-object descriptor ocean-cli deleteFile <bucketId> <fileName>
ocean-cli generateAuthToken
ocean-cli invalidateAuthToken myAuthToken123 # or --tokenocean-cli downloadNodeLogs ./logs 24 # or --output ./logs --last 24Writes <output>/logs.json. Use either --last (hours from now) or --from / --to (epoch ms); --maxLogs defaults to 100 (max 1000). Requires admin privileges on the node.
ocean-cli mintOcean [--token 0xOcean] [--chainId 8996] # mint test Ocean tokens (Barge / test chains)50 commands, as registered in src/cli.ts. <x> is required, [x] optional. A named option overrides its positional when both are passed, but a required <x> must still be given positionally (passing only its option fails with missing required argument — this affects e.g. publish, startCompute, startFreeCompute); optional [x] positionals may instead be supplied as named options. Commands with no Options entry (e.g. downloadJobResults) are positional-only. <command> --help is authoritative.
| Command | Aliases | Arguments | Options |
|---|---|---|---|
help |
h |
||
setNode |
useNode |
<nodeUrl> |
-n, --node |
setNodeEnv |
useNodeEnv |
<nodeEnv> |
|
getNode |
currentNode |
||
addChain |
addRpc |
<chainId> [rpcUrl...] |
-c, --chainId, -u, --url |
removeChain |
removeRpc |
<chainId> |
-c, --chainId |
listChains |
getRpcs, chains |
||
setChain |
useChain |
<chainId> |
-c, --chainId |
getChain |
currentChain |
||
getDDO |
<did> |
-d, --did |
|
publish |
<file> |
-f, --file, -e, --encrypt [bool] (default true) |
|
publishAlgo |
<file> |
-f, --file, -e, --encrypt [bool] |
|
editAsset |
edit |
<did> <file> |
-d, --did, -f, --file, -e, --encrypt [bool] |
download |
<did> [folder] [serviceId] |
-d, --did, -f, --folder (default .), -s, --service |
|
allowAlgo |
<datasetDid> <algoDid> |
-d, --dataset, -a, --algo, -e, --encrypt [bool] |
|
startCompute |
[datasets] [algo] [env] [maxJobDuration] [paymentToken] [resources] [output] [serviceIds] [algoServiceId] |
-d, --datasets, -a, --algo, -e, --env, --maxJobDuration, -t, --token, --resources, --chainId, --accept, -o, --output, -s, --services, -x, --algo-service |
|
startFreeCompute |
[datasets] [algo] [env] [output] [serviceIds] [algoServiceId] |
-d, --datasets, -a, --algo, -e, --env, -o, --output, -s, --services, -x, --algo-service, --chainId |
|
getComputeEnvironments |
getC2DEnvs |
[node] |
-n, --node |
searchComputeResources |
findComputeNodes |
--cpu --ram --disk --gpu --gpu-model --resource --free --paid --both --chain --token --max-price --duration --order-by |
|
computeStreamableLogs |
<jobId> |
-j, --job |
|
stopCompute |
<datasetDid> <jobId> <agreementId> |
-d, --dataset, -j, --job, -a, --agreement |
|
getJobStatus |
<datasetDid> <jobId> [agreementId] |
-d, --dataset, -j, --job, -a, --agreement |
|
downloadJobResults |
<jobId> <index> [folder] |
||
getServiceTemplates |
serviceTemplates |
[node] |
-n, --node |
startService |
<computeEnvId> <duration> <paymentToken> |
--template, -i, --image, --tag, --checksum, --dockerfile, --additional-docker-files, --cmd, --entrypoint, -p, --ports, -r, --resources, -u, --user-data, --user-data-file, --accept, --wait, --timeout, --chainId |
|
getServiceStatus |
myServices |
[serviceId] |
-s, --service, -v, --verbose |
getServices |
listServices |
[node] |
-n, --node, --status, --include-all, --from, -v, --verbose |
serviceLogs |
computeServiceLogs |
<serviceId> |
-s, --service, --since |
extendService |
<serviceId> <additionalDuration> [paymentToken] |
-s, --service, --duration, -t, --token, --accept, --chainId |
|
restartService |
<serviceId> |
-u, --user-data, --user-data-file, --cmd, --entrypoint, --image, --tag, --checksum, --dockerfile, --additional-docker-files, --wait, --timeout |
|
stopService |
<serviceId> |
-s, --service |
|
mintOcean |
-t, --token, --chainId |
||
generateAuthToken |
|||
invalidateAuthToken |
<token> |
-t, --token |
|
depositEscrow |
<token> <amount> |
-t, --token, -a, --amount, --chainId |
|
getUserFundsEscrow |
<token> |
-t, --token, --chainId |
|
withdrawFromEscrow |
<token> <amount> |
-t, --token, -a, --amount, --chainId |
|
authorizeEscrow |
<token> <payee> <maxLockedAmount> <maxLockSeconds> <maxLockCounts> |
-t, --token, -p, --payee, -m, --maxLockedAmount, -s, --maxLockSeconds, -c, --maxLockCounts, --chainId |
|
getAuthorizationsEscrow |
<token> <payee> |
-t, --token, -p, --payee, --chainId |
|
createAccessList |
<name> <symbol> [initialUsers] [transferable] |
-n, --name, -s, --symbol, -u, --initial-users, -t, --transferable, --chainId |
|
addToAccessList |
<address> <users> |
-a, --address, -u, --users, --chainId |
|
checkAccessList |
<address> <users> |
-a, --address, -u, --users, --chainId |
|
removeFromAccessList |
<address> <users> |
-a, --address, -u, --users, --chainId |
|
createBucket |
[accessListAddress] |
||
addFileToBucket |
<bucketId> <filePath> [fileName] |
||
listBuckets |
-o, --owner |
||
listFilesInBucket |
<bucketId> |
||
getFileObject |
<bucketId> <fileName> |
||
deleteFile |
<bucketId> <fileName> |
||
downloadNodeLogs |
<output> [last] [from] [to] |
-o, --output, -l, --last, -f, --from, -t, --to, -m, --maxLogs |
The metadata/ folder holds ready-to-publish DDOs. They default to chain 8996 (Barge); edit chainId and serviceEndpoint to match your network before publishing.
| File | DDO | What it is |
|---|---|---|
simpleDownloadDataset.json |
v4.1.0 | Dataset with an access service (URL file); the basic publish + download example |
simpleDownloadDatasetV5.json |
v5.0.0 | Same, as a v5 DDO (credentialSubject / SSI flows; see SSI_WALLET_*) |
downloadAssetPaymentUSDC.json |
v4.1.0 | Download dataset priced in USDC instead of the default token |
simpleComputeDataset.json |
v4.1.0 | Dataset with a compute service (URL file) |
simpleIPFSComputeDataset.json |
v4.1.0 | Compute dataset whose file is stored on IPFS |
jsAlgo.json |
v4.1.0 | Node.js algorithm (URL file) for compute |
jsAlgoV5.json |
v5.0.0 | Node.js algorithm as a v5 DDO |
jsIPFSAlgo.json |
v4.1.0 | Node.js algorithm stored on IPFS |
pythonAlgo.json |
v4.1.0 | Python algorithm (oceanprotocol/algo_dockers image) |
No Ocean Node set:NODE_URLis unset. Export it, or runsetNode <url>in the interactive prompt.- Node unreachable:
setNodekeeps your current node when the health check fails. Check the URL/peer id and port; for a local P2P node use the full multiaddr. Over P2P a bare peer id can take up to 30 s (DHT lookup); HTTP times out at 10 s. - Fails at startup with a
DISABLE_P2Pmessage:DISABLE_P2P=trueand a P2PNODE_URLcontradict each other. Unset one. - Wrong chain /
chainIderrors: the chain must be a registered RPC. RunlistChains, thenaddChain <chainId> <rpcUrl>.getNodeshows which chains the node serves. Pass--chainIdon chain-explicit commands, or set a default withsetChain. For DDO-implied commands the chain is the DDO'schainId, so fix it in your metadata file. - Escrow shortfall / service ends in
Error: deposit and authorize before starting (depositEscrow, thenauthorizeEscrowwithmaxLockSeconds >= duration + 3600).getUserFundsEscrowandgetAuthorizationsEscrowshow the current state. - Asset not indexed yet: the CLI waits up to
INDEXING_MAX_RETRIESxINDEXING_RETRY_INTERVAL(default 120 x 4 s). Raise them for slow chains. - Script hangs after the command: set
AVOID_LOOP_RUN=truefor one-shot runs. mintOceanasks for--token: the chain has no bundled Ocean token address.
git clone https://github.com/oceanprotocol/ocean-cli.git cd ocean-cli nvm use # .nvmrc pins Node 22.23.1 (needed for lint and release tooling) npm install npm run cli h # runs from TypeScript source via tsx, no build step; same as `ocean-cli h` npm run build
npm run cli <command> is equivalent to ocean-cli <command>. Tests (npm test) are mostly end-to-end and need a running Barge stack (RPC, Ocean Node, deployed contracts). Architecture notes and contributor guidance are in CLAUDE.md. Release history: CHANGELOG.md.
Copyright (C) 2026 Ocean Protocol Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
