Skip to main content
PolicyData oracles often need an API key to fetch external data. Newton lets you store those secrets encrypted so they are never exposed on-chain, and decrypts them only inside the operator at evaluation time — your WASM oracle reads them through a host interface without ever handling the ciphertext. This guide covers the full lifecycle: declaring which secrets an oracle needs, uploading them encrypted, and accessing them inside your oracle code. For the underlying encryption design, see Encrypting Secrets and the Privacy Layer.

1. Declare the secrets schema

Each PolicyData oracle declares the secrets it requires as a JSON Schema, stored on-chain as the oracle’s secretsSchemaCid. Uploaded secrets are validated against this schema before being stored. Create secrets_schema.json:
{
  "type": "object",
  "properties": {
    "VAULTS_FYI_API_KEY": { "type": "string", "minLength": 1 }
  },
  "required": ["VAULTS_FYI_API_KEY"],
  "additionalProperties": false
}
Pass it when generating CIDs so it is published with the oracle:
newton-cli policy-files generate-cids \
  -d ./dist \
  --entrypoint my_policy.allow \
  --secrets-schema-file ./secrets_schema.json \
  -o ./dist/policy_cids.json
An oracle that needs no secrets simply omits secretsSchemaCid (drop --secrets-schema-file).

2. Upload secrets (encrypted)

Secrets are encrypted client-side with HPKE before they ever leave your machine. The operator decrypts them in memory at evaluation time; the plaintext is never persisted and never sent over the network. Only the on-chain owner of the PolicyClient can upload secrets.
Secrets are scoped per policy_data_address. If you redeploy a PolicyData contract you get a new address, so you must re-upload its secrets. In a multi-oracle policy, upload each oracle’s secrets to its own PolicyData address.
# 1. Create a secrets file matching the oracle's schema
cat > secrets.json <<'EOF'
{ "VAULTS_FYI_API_KEY": "sk-prod-xxxxxxxxxxxx" }
EOF

# 2. Upload — the CLI fetches the HPKE key, encrypts locally, and uploads
newton-cli --chain-id 11155111 secrets upload \
  --secrets-file secrets.json \
  --policy-client 0xYourPolicyClient \
  --policy-data-address 0xYourPolicyData \
  --api-key $NEWTON_API_KEY
Both paths fetch the operator’s HPKE public key via newt_getSecretsPublicKey, seal the envelope locally, and submit it via newt_storeEncryptedSecrets. The operator validates the plaintext against your secretsSchemaCid and stores only the encrypted envelope.

3. Access secrets inside the oracle

Your WASM oracle reads secrets through the newton:provider/secrets host interface (declared in newton-provider.wit). Calling get() returns the decrypted secrets JSON as raw bytes — the operator has already decrypted them; you only decode and parse:
import { fetch as httpFetch } from "newton:provider/http@0.2.0";
import { get as getHostSecrets } from "newton:provider/secrets@0.2.0";

let _secrets = {};

// Load host-provided secrets once, at the start of run().
function loadHostSecrets() {
  try {
    const r = getHostSecrets();
    const resp = r?.val ?? r;       // unwrap result<secret-response, string>
    const bytes = resp?.value;      // value: list<u8> — decrypted secrets JSON
    if (!bytes || bytes.length === 0) return;
    const text = new TextDecoder().decode(new Uint8Array(bytes));
    const parsed = JSON.parse(text);
    if (parsed && typeof parsed === "object") {
      _secrets = { ..._secrets, ...parsed };
    }
  } catch (_) {
    // Host secrets unavailable (e.g. local sim without uploaded secrets).
  }
}

function secret(name) {
  return _secrets[name];
}

function getJson(url) {
  const apiKey = secret("VAULTS_FYI_API_KEY");
  const r = httpFetch({
    url,
    method: "GET",
    headers: [["accept", "application/json"], ["x-api-key", apiKey]],
    body: null,
  });
  if (r.tag === "err") throw new Error(`http: ${r.val}`);
  const resp = r.val ?? r;
  if (resp.status < 200 || resp.status >= 300) {
    throw new Error(`http ${resp.status}`);
  }
  return JSON.parse(new TextDecoder().decode(new Uint8Array(resp.body)));
}

export function run(input) {
  try {
    loadHostSecrets();
    const args = JSON.parse(input);
    const data = getJson(`https://api.example.com/v2/${args.id}`);
    // wrap output under your pack id so it composes with other oracles
    return JSON.stringify({ my_oracle: { value: data.value } });
  } catch (e) {
    return JSON.stringify({ my_oracle: { error: String(e) } });
  }
}
Notes:
  • get() returns a result<secret-response, string>; secret-response.value is list<u8> — the bytes of the decrypted secrets JSON object. The scope (which PolicyClient + PolicyData) is bound by the operator’s execution context, so you do not pass any identifiers.
  • The named keys you read (VAULTS_FYI_API_KEY above) must match the keys you uploaded and the properties in secrets_schema.json.
  • Put credentials in request headers, never in the URL.
  • Fail closed: if a fetch errors, return a namespaced error so your Rego can deny on it (see Chaining Data Oracles).
This is the exact pattern the Newton policy packs use. See the Writing Data Oracles guide for the full WIT interface and build steps.

4. Test that secrets resolve

Before submitting production tasks, verify the oracle reads its secrets correctly:
StepMethodPurpose
Iterate without uploadingnewt_simulatePolicyDataProvide secrets inline; no ownership required
Verify stored secretsnewt_simulatePolicyDataWithClientRuns the oracle with the secrets you uploaded (requires PolicyClient ownership)
# Verify stored secrets work for a deployed PolicyData
curl -X POST https://gateway.testnet.newton.xyz/rpc \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $NEWTON_API_KEY" \
  -d '{
    "jsonrpc": "2.0",
    "method": "newt_simulatePolicyDataWithClient",
    "params": {
      "policy_data_address": "0xYourPolicyData",
      "policy_client": "0xYourPolicyClient",
      "wasm_args": "0x7b226964223a2231323334227d"
    },
    "id": "7ca6621b-7aa4-4bb7-a896-1f2b58a18c78"
  }'
See Testing Policies & Oracles for the full simulation workflow.

Next Steps

Encrypting Secrets

The HPKE wire protocol and security model

Chaining Data Oracles

Per-oracle secret scoping in multi-oracle policies

Policy Packs

Prebuilt oracles and the keys each one needs

CLI Reference

newton-cli secrets commands and flags