Proxy Developer Guide

Proxy Developer Guide (End Developers)

This guide is for application developers integrating encrypted key/value and file operations through Harden Proxy.

Why Use Proxy API

Use this API when your app must store secrets or files but you do not want plaintext data persisted on the backend.

Benefits:

  • Client-side style protection with server-side API simplicity.
  • Backend stores encrypted payloads and wrapped keys, not plaintext.
  • User-scoped access with API keys and user-secret based unwrap.
  • Unified API for encrypted key/value and encrypted file workflows.

Service Endpoint

  • Dev endpoint: https://dev2-api.harden.cloud
  • Required header: X-Api-Key: <user_api_key>
  • Key management and read operations also require: X-User-Secret: <user_secret>

Core Workflows

1. Check API health

1
curl -sS https://dev2-api.harden.cloud/health
1
2
const healthRes = await fetch("https://dev2-api.harden.cloud/health");
console.log(await healthRes.json());

2. Check key status

1
2
3
curl -sS \
  -H "X-Api-Key: ${API_KEY}" \
  https://dev2-api.harden.cloud/v1/keys/status
1
2
3
4
5
const statusRes = await fetch("https://dev2-api.harden.cloud/v1/keys/status", {
  headers: { "X-Api-Key": apiKey }
});
const keyStatus = await statusRes.json();
console.log(keyStatus);

3. Generate keys for a user (first-time)

1
2
3
4
curl -sS -X POST \
  -H "X-Api-Key: ${API_KEY}" \
  -H "X-User-Secret: ${USER_SECRET}" \
  https://dev2-api.harden.cloud/v1/keys/generate
1
2
3
4
5
6
7
await fetch("https://dev2-api.harden.cloud/v1/keys/generate", {
  method: "POST",
  headers: {
    "X-Api-Key": apiKey,
    "X-User-Secret": userSecret
  }
});

4. Save a value (encrypted by Proxy)

1
2
3
4
5
curl -sS -X PUT \
  -H "X-Api-Key: ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"valueB64":"aHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20="}' \
  "https://dev2-api.harden.cloud/v1/kv/app%2Fconfig%2Fbase-url"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const plaintext = "https://api.example.com";
const valueB64 = btoa(plaintext);

await fetch("https://dev2-api.harden.cloud/v1/kv/app%2Fconfig%2Fbase-url", {
  method: "PUT",
  headers: {
    "X-Api-Key": apiKey,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ valueB64 })
});

5. List keys

1
2
3
curl -sS \
  -H "X-Api-Key: ${API_KEY}" \
  https://dev2-api.harden.cloud/v1/kv
1
2
3
4
const keysRes = await fetch("https://dev2-api.harden.cloud/v1/kv", {
  headers: { "X-Api-Key": apiKey }
});
const keys = await keysRes.json();

6. Read one value

1
2
3
4
curl -sS \
  -H "X-Api-Key: ${API_KEY}" \
  -H "X-User-Secret: ${USER_SECRET}" \
  "https://dev2-api.harden.cloud/v1/kv/app%2Fconfig%2Fbase-url"
1
2
3
4
5
6
7
8
9
const readRes = await fetch("https://dev2-api.harden.cloud/v1/kv/app%2Fconfig%2Fbase-url", {
  headers: {
    "X-Api-Key": apiKey,
    "X-User-Secret": userSecret
  }
});
const readPayload = await readRes.json();
const value = atob(readPayload.valueB64);
console.log(value);

7. Delete a value

1
2
3
curl -sS -X DELETE \
  -H "X-Api-Key: ${API_KEY}" \
  "https://dev2-api.harden.cloud/v1/kv/app%2Fconfig%2Fbase-url"
1
2
3
4
await fetch("https://dev2-api.harden.cloud/v1/kv/app%2Fconfig%2Fbase-url", {
  method: "DELETE",
  headers: { "X-Api-Key": apiKey }
});

File API

Files use the same user API key and encryption model.

Upload file

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
curl -sS -X POST \
  -H "X-Api-Key: ${API_KEY}" \
  -H "X-User-Secret: ${USER_SECRET}" \
  -H "Content-Type: application/json" \
  -d '{
    "fileName":"credentials.json",
    "contentB64":"ewogICJ0b2tlbiI6ICJhYmMiCn0=",
    "contentType":"application/json"
  }' \
  https://dev2-api.harden.cloud/v1/files
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
const bytes = new TextEncoder().encode(JSON.stringify({ token: "abc" }));
const contentB64 = btoa(String.fromCharCode(...bytes));

await fetch("https://dev2-api.harden.cloud/v1/files", {
  method: "POST",
  headers: {
    "X-Api-Key": apiKey,
    "X-User-Secret": userSecret,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    fileName: "credentials.json",
    contentB64,
    contentType: "application/json"
  })
});

List files

1
2
3
4
curl -sS \
  -H "X-Api-Key: ${API_KEY}" \
  -H "X-User-Secret: ${USER_SECRET}" \
  "https://dev2-api.harden.cloud/v1/files"
1
2
3
4
5
6
7
const filesRes = await fetch("https://dev2-api.harden.cloud/v1/files", {
  headers: {
    "X-Api-Key": apiKey,
    "X-User-Secret": userSecret
  }
});
const files = await filesRes.json();

Download one file

1
2
3
4
curl -sS \
  -H "X-Api-Key: ${API_KEY}" \
  -H "X-User-Secret: ${USER_SECRET}" \
  "https://dev2-api.harden.cloud/v1/files/${FILE_ID}"
1
2
3
4
5
6
7
const downloadRes = await fetch(`https://dev2-api.harden.cloud/v1/files/${fileId}`, {
  headers: {
    "X-Api-Key": apiKey,
    "X-User-Secret": userSecret
  }
});
const filePayload = await downloadRes.json();

Delete one file

1
2
3
curl -sS -X DELETE \
  -H "X-Api-Key: ${API_KEY}" \
  https://dev2-api.harden.cloud/v1/files/${FILE_ID}
1
2
3
4
await fetch(`https://dev2-api.harden.cloud/v1/files/${fileId}`, {
  method: "DELETE",
  headers: { "X-Api-Key": apiKey }
});

Error Handling

  • 401 or 403: invalid API key, expired session, or insufficient role.
  • 400: invalid payload, missing user secret, or plan/storage validation failure.
  • 404: key/file not found.
  • 429: apply client retry/backoff.
  • 5xx: transient server issue; use idempotent retries.

Security Guidance

  • Never log plaintext secrets, userSecret, or raw payloads.
  • Keep API keys in a secrets manager; rotate if leaked.
  • Use TLS-only transport and pin hostnames in production clients.
  • Store user secret only in secure session memory, not long-lived storage.
  1. Authenticate user in your app.
  2. Load API key from secure backend for that user context.
  3. Prompt for user secret only when crypto operations are needed.
  4. Cache minimal session state and clear on logout.
  5. Retry idempotent operations with exponential backoff.