# Encrypting Secrets \[Securely store API keys and credentials for Newton Protocol PolicyData oracles]

PolicyData oracles often need access to API keys and credentials to fetch external data. Newton Protocol's [Privacy Layer](/developers/concepts/privacy-layer) encrypts these secrets client-side using HPKE (X25519 + HKDF-SHA256 + ChaCha20-Poly1305) so they are never exposed on-chain or to unauthorized parties.

## Overview

The secret storage flow:

1. **Prepare your secrets** as a JSON object matching your PolicyData's schema
2. **Call `storeEncryptedSecrets`** from the SDK — it encrypts the plaintext locally and uploads the HPKE envelope
3. **Operators decrypt** the envelope during task evaluation to run your WASM oracle

Secrets are validated against the on-chain schema (`secretsSchemaCid`) defined on your PolicyData contract.

## Secrets Format

Prepare a JSON object matching your PolicyData's secrets schema:

```json
{
  "API_KEY": "sk-xxxxxxxxxxxxx",
  "ENDPOINT": "https://api.example.com"
}
```

## Store via SDK

Use the `storeEncryptedSecrets` function from the SDK. It fetches the gateway's HPKE public key automatically (or accepts one you've cached), encrypts the plaintext locally, and uploads the envelope in a single call.

```ts twoslash
import { storeEncryptedSecrets } from '@newton-xyz/sdk'

const result = await storeEncryptedSecrets(
  11155111, // chainId
  'your-api-key',
  {
    policyClient: '0xYourPolicyClientAddress',
    policyDataAddress: '0xYourPolicyDataAddress',
    plaintext: {
      API_KEY: 'sk-xxxxxxxxxxxxx',
      ENDPOINT: 'https://api.example.com',
    },
    chainId: 11155111,
    // recipientPublicKey: '...' — optional, fetched automatically if omitted
  },
)

if (!result.success) {
  throw new Error(`Upload failed: ${result.error}`)
}
```

Secrets are validated against the on-chain schema before storing.

## Reference in PolicyData

Your WASM oracle reads secrets at runtime through the `newton:provider/secrets` host interface. Operators decrypt the stored envelope before execution; `get()` returns the decrypted secrets JSON as raw bytes, which you decode and parse.

```js
// In your WASM oracle (policy.js)
import { fetch as httpFetch } from "newton:provider/http@0.2.0";
import { get as getHostSecrets } from "newton:provider/secrets@0.2.0";

let _secrets = {};

function loadHostSecrets() {
  const r = getHostSecrets();              // result<secret-response, string>
  const resp = r?.val ?? r;
  const bytes = resp?.value;               // value: list<u8> — decrypted secrets JSON
  if (!bytes || bytes.length === 0) return;
  const text = new TextDecoder().decode(new Uint8Array(bytes));
  _secrets = { ..._secrets, ...JSON.parse(text) };
}

export function run(wasm_args) {
  loadHostSecrets();

  const response = httpFetch({
    url: "https://api.example.com/data",
    method: "GET",
    headers: [["Authorization", `Bearer ${_secrets.API_KEY}`]],
    body: null,
  });

  if (response.tag === "err") {
    return JSON.stringify({ error: response.val });
  }

  const body = new TextDecoder().decode(new Uint8Array(response.val.body));
  return JSON.stringify(JSON.parse(body));
}
```

:::note
For a full walkthrough of declaring, uploading, and reading secrets — plus per-oracle scoping in multi-oracle policies — see [Uploading & Accessing Secrets in Oracles](/developers/guides/secrets-in-oracles).
:::

## Secrets Schema

The `secretsSchemaCid` on your PolicyData contract points to a JSON Schema that defines the required shape of your secrets:

```json
{
  "type": "object",
  "properties": {
    "API_KEY": { "type": "string" },
    "ENDPOINT": { "type": "string", "format": "uri" }
  },
  "required": ["API_KEY", "ENDPOINT"]
}
```

If your uploaded secrets do not match this schema, `newt_storeEncryptedSecrets` returns a validation error.

## Security

* Secrets are encrypted client-side before leaving your application — the HPKE envelope is what travels over the wire
* The gateway decrypts the envelope in memory to validate against the schema, then stores the envelope (not the plaintext)
* Only the on-chain owner of the PolicyClient can upload or update secrets
* Operators decrypt the stored envelope locally during task evaluation — plaintext secrets are never persisted by operators and never sent over the network
* Secrets are scoped **per `policy_data_address`** — redeploying a PolicyData contract produces a new address, so re-upload its secrets. In a multi-oracle policy, upload each oracle's secrets to its own PolicyData address
* Key rotation is supported — upload new secrets at any time via `newt_storeEncryptedSecrets`

For a deeper look at Newton's encryption architecture, see the [Privacy Layer](/developers/concepts/privacy-layer).

## Testing

Test your secrets work correctly before deploying:

1. **Test with inline secrets** (no ownership required):
   ```bash
   # newt_simulatePolicyData — provide secrets directly
   ```

2. **Test with stored secrets** (requires ownership):
   ```bash
   # newt_simulatePolicyDataWithClient — uses stored secrets
   ```

See the [RPC API](/developers/reference/rpc-api) for full parameter details.

## Next Steps

<Card icon="lock" to="/developers/concepts/privacy-layer" title="Privacy Layer">
  Full privacy architecture overview
</Card>

<Card icon="plug" to="/developers/reference/rpc-api" title="RPC API">
  newt\_storeEncryptedSecrets and related methods
</Card>
